@ohkit/back-top 0.0.1
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/LICENSE.md +5 -0
- package/README.md +111 -0
- package/dist/index.css +2 -0
- package/dist/index.css.map +1 -0
- package/dist/index.es.js +2 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.modern.mjs +2 -0
- package/dist/index.modern.mjs.map +1 -0
- package/dist/index.umd.js +2 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/types/index.d.ts +40 -0
- package/package.json +39 -0
- package/src/index.tsx +135 -0
- package/src/style.scss +46 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Copyright (c) [2025], [wuqiuyang]
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
4
|
+
|
|
5
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# @ohkit/back-top
|
|
2
|
+
|
|
3
|
+
返回顶部组件。当页面或容器滚动超过指定距离后自动显示返回顶部按钮,点击后平滑滚动到顶部。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @ohkit/back-top
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 使用
|
|
12
|
+
|
|
13
|
+
### 基本用法
|
|
14
|
+
```tsx
|
|
15
|
+
import { BackTop } from '@ohkit/back-top';
|
|
16
|
+
import '@ohkit/back-top/dist/index.css';
|
|
17
|
+
|
|
18
|
+
function App() {
|
|
19
|
+
return (
|
|
20
|
+
<div style={{ height: '100vh', overflow: 'auto' }}>
|
|
21
|
+
<div style={{ minHeight: '2000px' }}>
|
|
22
|
+
{/* 很长的高度内容 */}
|
|
23
|
+
</div>
|
|
24
|
+
{/* 组件放入滚动容器中,自动查找所属的滚动容器 */}
|
|
25
|
+
<BackTop />
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 指定滚动容器
|
|
32
|
+
```tsx
|
|
33
|
+
import { useRef } from 'react';
|
|
34
|
+
import { BackTop } from '@ohkit/back-top';
|
|
35
|
+
|
|
36
|
+
function CustomContainer() {
|
|
37
|
+
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<section>
|
|
41
|
+
<div ref={scrollContainerRef} style={{ height: '400px', overflow: 'auto' }}>
|
|
42
|
+
<div style={{ minHeight: '800px' }}>
|
|
43
|
+
{/* 内容 */}
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
<BackTop
|
|
47
|
+
scrollRefDom={scrollContainerRef}
|
|
48
|
+
mountType="absolute"
|
|
49
|
+
/>
|
|
50
|
+
</section>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 自定义按钮内容
|
|
56
|
+
```tsx
|
|
57
|
+
<BackTop title="回到顶部" children={<>↑<br/>顶部</>} />
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## API
|
|
61
|
+
|
|
62
|
+
### BackTop Props
|
|
63
|
+
|
|
64
|
+
| 属性 | 类型 | 默认值 | 说明 |
|
|
65
|
+
|------|------|--------|------|
|
|
66
|
+
| className | string | '' | 自定义类名 |
|
|
67
|
+
| children | ReactNode | '↑' | 按钮内容,支持React节点 |
|
|
68
|
+
| mountType | 'fixed' \| 'absolute' | 'fixed' | 按钮定位方式 |
|
|
69
|
+
| scrollTop | number | 500 | 显示按钮的滚动阈值(像素) |
|
|
70
|
+
| scrollRefDom | React.RefObject<HTMLElement> | - | 自定义滚动容器 |
|
|
71
|
+
| position | 'top-right' \| 'top-left' \| 'bottom-right' \| 'bottom-left' | 'bottom-right' | 按钮位置 |
|
|
72
|
+
| offset | [number, number] | [50, 100] | 按钮偏移量 [top/bottom, left/right] |
|
|
73
|
+
| realScroll | boolean | false | 是否查找真实可滚动容器 |
|
|
74
|
+
| title | string | '返回顶部' | 按钮提示文本 |
|
|
75
|
+
|
|
76
|
+
## 特性说明
|
|
77
|
+
|
|
78
|
+
### 自动滚动容器检测
|
|
79
|
+
组件会自动查找包含它的第一个滚动容器。如果设置 `realScroll={true}`,会查找 `scrollHeight > clientHeight` 的真实滚动容器。
|
|
80
|
+
|
|
81
|
+
### 平滑滚动
|
|
82
|
+
点击按钮时会使用 `scrollTo({ top: 0, behavior: 'smooth' })` 实现平滑滚动效果。
|
|
83
|
+
|
|
84
|
+
### 性能优化
|
|
85
|
+
- 使用防抖机制避免频繁的滚动事件处理
|
|
86
|
+
- 只有滚动状态变化时才触发重渲染
|
|
87
|
+
|
|
88
|
+
## 依赖说明
|
|
89
|
+
|
|
90
|
+
组件依赖以下工具包:
|
|
91
|
+
- `@ohkit/dom-helper`:DOM事件处理
|
|
92
|
+
- `@ohkit/prefix-classname`:CSS类名前缀
|
|
93
|
+
- `@ohkit/react-helper`:React运行时工具
|
|
94
|
+
|
|
95
|
+
## 样式定制
|
|
96
|
+
|
|
97
|
+
组件支持通过 `className` 属性进行样式覆盖:
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
<BackTop className="custom-back-top" />
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
然后通过CSS或CSS-in-JS定义样式:
|
|
104
|
+
|
|
105
|
+
```css
|
|
106
|
+
.custom-back-top {
|
|
107
|
+
background: #007bff;
|
|
108
|
+
border-radius: 8px;
|
|
109
|
+
/* 其他自定义样式 */
|
|
110
|
+
}
|
|
111
|
+
```
|
package/dist/index.css
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
.ohkit-back-top__container{align-items:center;border-radius:4px;box-sizing:border-box;color:#fff;display:flex;font-size:14px;justify-content:center;line-height:1.2em;min-height:40px;min-width:40px;padding:4px;position:fixed;transition:all .2s ease-in;white-space:pre-line;z-index:10}.ohkit-back-top__absolute{bottom:20px;position:absolute;right:20px}.ohkit-back-top__invisible{background:transparent;opacity:0;pointer-events:none}.ohkit-back-top__visible{background:rgba(78,85,98,.25);cursor:pointer;opacity:1}.ohkit-back-top__visible:hover{background:rgba(78,85,98,.4)}
|
|
2
|
+
/*# sourceMappingURL=index.css.map */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["style.scss"],"names":[],"mappings":"AAAA,2BASE,kBAAmB,CAKnB,iBAAkB,CAZlB,qBAAsB,CAUtB,UAAW,CAJX,YAAa,CAGb,cAAe,CADf,sBAAuB,CAJvB,iBAAkB,CADlB,eAAgB,CADhB,cAAe,CADf,WAAY,CAFZ,cAAe,CAcf,0BAA4B,CAF5B,oBAAqB,CANrB,UASF,CACA,0BAGE,WAAY,CAFZ,iBAAkB,CAClB,UAEF,CACA,2BAGE,sBAAuB,CADvB,SAAU,CADV,mBAGF,CACA,yBACE,6BAAkC,CAElC,cAAe,CADf,SAEF,CACA,+BACE,4BACF","file":"index.css","sourcesContent":[".ohkit-back-top__container {\n position: fixed;\n box-sizing: border-box;\n padding: 4px;\n min-width: 40px;\n min-height: 40px;\n line-height: 1.2em;\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 14px;\n color: #fff;\n white-space: pre-line;\n border-radius: 4px;\n transition: all 0.2s ease-in;\n}\n.ohkit-back-top__absolute {\n position: absolute;\n right: 20px;\n bottom: 20px;\n}\n.ohkit-back-top__invisible {\n pointer-events: none;\n opacity: 0;\n background: transparent;\n}\n.ohkit-back-top__visible {\n background: rgba(78, 85, 98, 0.25);\n opacity: 1;\n cursor: pointer;\n}\n.ohkit-back-top__visible:hover {\n background: rgba(78, 85, 98, 0.4);\n}"]}
|
package/dist/index.es.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import o,{useState as r,useRef as e,useMemo as l,useEffect as i}from"react";import{prefixClassname as t,classNames as s}from"@ohkit/prefix-classname";import{useRuntime as n,useScrollEntry as c}from"@ohkit/react-helper";import{addEventListener as a}from"@ohkit/dom-helper";var m=t("ohkit-back-top__"),v=function(t){var v=t.children,p=t.className,u=t.scrollTop,f=void 0===u?500:u,d=t.mountType,b=void 0===d?"fixed":d,h=t.position,R=void 0===h?"bottom-right":h,k=t.offset,L=void 0===k?[50,100]:k,x=t.title,T=void 0===x?"返回顶部":x,y=t.scrollRefDom,V=t.realScroll,g=void 0!==V&&V,C=r(!1),E=C[0],N=C[1],S=e(null),_=n({preVisible:E,scrollListenerRemover:null})[0],D=c(y||S,{realScroll:g,autoFind:!0,intervalTime:3e3}),F=l(function(){var o,r=R.split("-"),e=r[1];return(o={})[r[0]]=L[0]+"px",o[e]=L[1]+"px",o},[R,L]);return i(function(){if(console.log("scrollEntry",D),D&&D.scroller){null==_.scrollListenerRemover||_.scrollListenerRemover();var o=function(){var o=D.scrollContainer,r=!!(o&&o.scrollTop>f);_.preVisible!==r&&(_.preVisible=r,N(r))};return o(),_.scrollListenerRemover=a(D.scroller,"scroll",o),function(){null==_.scrollListenerRemover||_.scrollListenerRemover()}}},[D.scroller]),/*#__PURE__*/o.createElement("div",{title:T,ref:S,style:F,className:s(m("container",{visible:!!E,invisible:!E}),m({absolute:"absolute"===b}),p),onClick:function(){var o;null==(o=D.scroller)||o.scrollTo({top:0,behavior:"smooth"})}},v||"↑")};export{v as BackTop,m as c};
|
|
2
|
+
//# sourceMappingURL=index.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.es.js","sources":["../src/index.tsx"],"sourcesContent":["import React, { PropsWithChildren, useEffect, useState, useMemo, useRef } from \"react\";\nimport {\n prefixClassname as p,\n classNames as cx,\n} from \"@ohkit/prefix-classname\";\nimport { useRuntime, useScrollEntry } from \"@ohkit/react-helper\";\nimport { addEventListener } from \"@ohkit/dom-helper\";\n\nimport \"./style.scss\";\n\nexport const c = p(\"ohkit-back-top__\");\n\nexport interface IBackTop {\n className?: string;\n /**\n * 按钮位置,fixed 或 absolute\n * @default \"fixed\"\n */\n mountType?: \"fixed\" | \"absolute\";\n /**\n * 滚动距离,当滚动距离大于此值时显示返回顶部按钮\n * @default 500\n */\n scrollTop?: number;\n scrollRefDom?: React.MutableRefObject<HTMLElement>;\n /**\n * 按钮位置,top-right 或 top-left 或 bottom-right 或 bottom-left\n * @default \"bottom-right\"\n */\n position?: \"top-right\" | \"top-left\" | \"bottom-right\" | \"bottom-left\";\n /**\n * 按钮位置偏移量px, 即[position.split(\"-\")[0], position.split(\"-\")[1]]\n * @default[50, 100]\n */\n offset?: [number, number];\n /**\n * 是否查找真实可滚动的容器,scrollHeight > clientHeight\n * @default false\n */\n realScroll?: boolean;\n /**\n * 按钮标题\n * @default \"返回顶部\"\n */\n title?: string;\n}\n\nexport type BackTopProps = PropsWithChildren<IBackTop>;\n\nexport const BackTop = ({\n children,\n className,\n scrollTop = 500,\n mountType = \"fixed\",\n position = \"bottom-right\",\n offset = [50, 100],\n title = \"返回顶部\",\n scrollRefDom,\n realScroll = false,\n}: BackTopProps) => {\n const [visible, setVisible] = useState(false);\n const domRef = useRef<HTMLDivElement>(null);\n const [runtime] = useRuntime<{\n preVisible: boolean;\n scrollListenerRemover: null | (() => void);\n }>({\n preVisible: visible,\n scrollListenerRemover: null,\n });\n\n const scrollEntry = useScrollEntry(scrollRefDom ? scrollRefDom : domRef, {\n realScroll,\n autoFind: true,\n intervalTime: 3000,\n });\n\n const positionStyle = useMemo(() => {\n const [x, y] = position.split(\"-\");\n return {\n [x]: `${offset[0]}px`,\n [y]: `${offset[1]}px`,\n };\n }, [position, offset]);\n\n useEffect(() => {\n console.log('scrollEntry', scrollEntry);\n if (scrollEntry && scrollEntry.scroller) {\n runtime.scrollListenerRemover?.();\n const onScroll = () => {\n const { scrollContainer } = scrollEntry;\n const curVisible = !!(\n scrollContainer &&\n (scrollContainer as HTMLElement).scrollTop > scrollTop\n );\n if (runtime.preVisible !== curVisible) {\n runtime.preVisible = curVisible;\n setVisible(curVisible);\n }\n };\n onScroll();\n runtime.scrollListenerRemover = addEventListener(\n scrollEntry.scroller,\n \"scroll\",\n onScroll\n );\n return () => {\n runtime.scrollListenerRemover?.();\n };\n }\n }, [scrollEntry.scroller]);\n\n return (\n <div\n title={title}\n ref={domRef}\n style={positionStyle}\n className={cx(\n c(\"container\", {\n visible: !!visible,\n invisible: !visible,\n }),\n c({ absolute: mountType === \"absolute\" }),\n className\n )}\n onClick={() => {\n scrollEntry.scroller?.scrollTo({\n top: 0,\n behavior: \"smooth\",\n });\n }}\n >\n {children || '↑'}\n </div>\n );\n};\n"],"names":["c","p","BackTop","_ref","children","className","_ref$scrollTop","scrollTop","_ref$mountType","mountType","_ref$position","position","_ref$offset","offset","_ref$title","title","scrollRefDom","_ref$realScroll","realScroll","_useState","useState","visible","setVisible","domRef","useRef","runtime","useRuntime","preVisible","scrollListenerRemover","scrollEntry","useScrollEntry","autoFind","intervalTime","positionStyle","useMemo","_ref2","_position$split","split","y","useEffect","console","log","scroller","onScroll","scrollContainer","curVisible","addEventListener","React","createElement","ref","style","cx","invisible","absolute","onClick","_scrollEntry$scroller","scrollTo","top","behavior"],"mappings":"gRAUa,IAAAA,EAAIC,EAAE,oBAuCNC,EAAU,SAAHC,GAUD,IATjBC,EAAQD,EAARC,SACAC,EAASF,EAATE,UAASC,EAAAH,EACTI,UAAAA,OAAY,IAAHD,EAAG,IAAGA,EAAAE,EAAAL,EACfM,UAAAA,OAAY,IAAHD,EAAG,QAAOA,EAAAE,EAAAP,EACnBQ,SAAAA,OAAW,IAAHD,EAAG,eAAcA,EAAAE,EAAAT,EACzBU,OAAAA,OAAM,IAAAD,EAAG,CAAC,GAAI,KAAIA,EAAAE,EAAAX,EAClBY,MAAAA,OAAK,IAAAD,EAAG,OAAMA,EACdE,EAAYb,EAAZa,aAAYC,EAAAd,EACZe,WAAAA,OAAU,IAAAD,GAAQA,EAElBE,EAA8BC,GAAS,GAAhCC,EAAOF,EAAA,GAAEG,EAAUH,EAAA,GACpBI,EAASC,EAAuB,MAC/BC,EAAWC,EAGf,CACDC,WAAYN,EACZO,sBAAuB,OALX,GAQRC,EAAcC,EAAed,GAA8BO,EAAQ,CACvEL,WAAAA,EACAa,UAAU,EACVC,aAAc,MAGVC,EAAgBC,EAAQ,WAAK,IAAAC,EACjCC,EAAezB,EAAS0B,MAAM,KAApBC,EAACF,EACX,GAAA,OAAAD,EAAA,CAAA,GADQC,EAAEE,IAEAzB,EAAO,GAAEsB,KAAAA,EAChBG,GAAOzB,EAAO,GAAE,KAAAsB,CAErB,EAAG,CAACxB,EAAUE,IA6Bd,OA3BA0B,EAAU,WAER,GADAC,QAAQC,IAAI,cAAeZ,GACvBA,GAAeA,EAAYa,SAAU,CACV,MAA7BjB,EAAQG,uBAARH,EAAQG,wBACR,IAAMe,EAAW,WACf,IAAQC,EAAoBf,EAApBe,gBACFC,KACJD,GACCA,EAAgCrC,UAAYA,GAE3CkB,EAAQE,aAAekB,IACzBpB,EAAQE,WAAakB,EACrBvB,EAAWuB,GAEf,EAOA,OANAF,IACAlB,EAAQG,sBAAwBkB,EAC9BjB,EAAYa,SACZ,SACAC,GAEK,WACLlB,MAAAA,EAAQG,uBAARH,EAAQG,uBACV,CACD,CACH,EAAG,CAACC,EAAYa,wBAGZK,EAAAC,cAAA,MAAA,CACEjC,MAAOA,EACPkC,IAAK1B,EACL2B,MAAOjB,EACP5B,UAAW8C,EACTnD,EAAE,YAAa,CACbqB,UAAWA,EACX+B,WAAY/B,IAEdrB,EAAE,CAAEqD,SAAwB,aAAd5C,IACdJ,GAEFiD,QAAS,WAAK,IAAAC,EACZA,OAAAA,EAAA1B,EAAYa,WAAZa,EAAsBC,SAAS,CAC7BC,IAAK,EACLC,SAAU,UAEd,GAECtD,GAAY,IAGrB"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=require("react"),r=require("@ohkit/prefix-classname"),l=require("@ohkit/react-helper"),o=require("@ohkit/dom-helper");function t(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var i=/*#__PURE__*/t(e),s=r.prefixClassname("ohkit-back-top__");exports.BackTop=function(t){var n=t.children,c=t.className,a=t.scrollTop,u=void 0===a?500:a,v=t.mountType,f=void 0===v?"fixed":v,m=t.position,p=void 0===m?"bottom-right":m,d=t.offset,b=void 0===d?[50,100]:d,h=t.title,R=void 0===h?"返回顶部":h,k=t.scrollRefDom,x=t.realScroll,L=void 0!==x&&x,T=e.useState(!1),y=T[0],E=T[1],q=e.useRef(null),S=l.useRuntime({preVisible:y,scrollListenerRemover:null})[0],C=l.useScrollEntry(k||q,{realScroll:L,autoFind:!0,intervalTime:3e3}),N=e.useMemo(function(){var e,r=p.split("-"),l=r[1];return(e={})[r[0]]=b[0]+"px",e[l]=b[1]+"px",e},[p,b]);return e.useEffect(function(){if(console.log("scrollEntry",C),C&&C.scroller){null==S.scrollListenerRemover||S.scrollListenerRemover();var e=function(){var e=C.scrollContainer,r=!!(e&&e.scrollTop>u);S.preVisible!==r&&(S.preVisible=r,E(r))};return e(),S.scrollListenerRemover=o.addEventListener(C.scroller,"scroll",e),function(){null==S.scrollListenerRemover||S.scrollListenerRemover()}}},[C.scroller]),/*#__PURE__*/i.default.createElement("div",{title:R,ref:q,style:N,className:r.classNames(s("container",{visible:!!y,invisible:!y}),s({absolute:"absolute"===f}),c),onClick:function(){var e;null==(e=C.scroller)||e.scrollTo({top:0,behavior:"smooth"})}},n||"↑")},exports.c=s;
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.tsx"],"sourcesContent":["import React, { PropsWithChildren, useEffect, useState, useMemo, useRef } from \"react\";\nimport {\n prefixClassname as p,\n classNames as cx,\n} from \"@ohkit/prefix-classname\";\nimport { useRuntime, useScrollEntry } from \"@ohkit/react-helper\";\nimport { addEventListener } from \"@ohkit/dom-helper\";\n\nimport \"./style.scss\";\n\nexport const c = p(\"ohkit-back-top__\");\n\nexport interface IBackTop {\n className?: string;\n /**\n * 按钮位置,fixed 或 absolute\n * @default \"fixed\"\n */\n mountType?: \"fixed\" | \"absolute\";\n /**\n * 滚动距离,当滚动距离大于此值时显示返回顶部按钮\n * @default 500\n */\n scrollTop?: number;\n scrollRefDom?: React.MutableRefObject<HTMLElement>;\n /**\n * 按钮位置,top-right 或 top-left 或 bottom-right 或 bottom-left\n * @default \"bottom-right\"\n */\n position?: \"top-right\" | \"top-left\" | \"bottom-right\" | \"bottom-left\";\n /**\n * 按钮位置偏移量px, 即[position.split(\"-\")[0], position.split(\"-\")[1]]\n * @default[50, 100]\n */\n offset?: [number, number];\n /**\n * 是否查找真实可滚动的容器,scrollHeight > clientHeight\n * @default false\n */\n realScroll?: boolean;\n /**\n * 按钮标题\n * @default \"返回顶部\"\n */\n title?: string;\n}\n\nexport type BackTopProps = PropsWithChildren<IBackTop>;\n\nexport const BackTop = ({\n children,\n className,\n scrollTop = 500,\n mountType = \"fixed\",\n position = \"bottom-right\",\n offset = [50, 100],\n title = \"返回顶部\",\n scrollRefDom,\n realScroll = false,\n}: BackTopProps) => {\n const [visible, setVisible] = useState(false);\n const domRef = useRef<HTMLDivElement>(null);\n const [runtime] = useRuntime<{\n preVisible: boolean;\n scrollListenerRemover: null | (() => void);\n }>({\n preVisible: visible,\n scrollListenerRemover: null,\n });\n\n const scrollEntry = useScrollEntry(scrollRefDom ? scrollRefDom : domRef, {\n realScroll,\n autoFind: true,\n intervalTime: 3000,\n });\n\n const positionStyle = useMemo(() => {\n const [x, y] = position.split(\"-\");\n return {\n [x]: `${offset[0]}px`,\n [y]: `${offset[1]}px`,\n };\n }, [position, offset]);\n\n useEffect(() => {\n console.log('scrollEntry', scrollEntry);\n if (scrollEntry && scrollEntry.scroller) {\n runtime.scrollListenerRemover?.();\n const onScroll = () => {\n const { scrollContainer } = scrollEntry;\n const curVisible = !!(\n scrollContainer &&\n (scrollContainer as HTMLElement).scrollTop > scrollTop\n );\n if (runtime.preVisible !== curVisible) {\n runtime.preVisible = curVisible;\n setVisible(curVisible);\n }\n };\n onScroll();\n runtime.scrollListenerRemover = addEventListener(\n scrollEntry.scroller,\n \"scroll\",\n onScroll\n );\n return () => {\n runtime.scrollListenerRemover?.();\n };\n }\n }, [scrollEntry.scroller]);\n\n return (\n <div\n title={title}\n ref={domRef}\n style={positionStyle}\n className={cx(\n c(\"container\", {\n visible: !!visible,\n invisible: !visible,\n }),\n c({ absolute: mountType === \"absolute\" }),\n className\n )}\n onClick={() => {\n scrollEntry.scroller?.scrollTo({\n top: 0,\n behavior: \"smooth\",\n });\n }}\n >\n {children || '↑'}\n </div>\n );\n};\n"],"names":["c","p","prefixClassname","_ref","children","className","_ref$scrollTop","scrollTop","_ref$mountType","mountType","_ref$position","position","_ref$offset","offset","_ref$title","title","scrollRefDom","_ref$realScroll","realScroll","_useState","useState","visible","setVisible","domRef","useRef","runtime","useRuntime","preVisible","scrollListenerRemover","scrollEntry","useScrollEntry","autoFind","intervalTime","positionStyle","useMemo","_ref2","_position$split","split","y","useEffect","console","log","scroller","onScroll","scrollContainer","curVisible","addEventListener","React","createElement","ref","style","cx","invisible","absolute","onClick","_scrollEntry$scroller","scrollTo","top","behavior"],"mappings":"4NAUaA,EAAIC,EAACC,gBAAC,oCAuCI,SAAHC,GAUD,IATjBC,EAAQD,EAARC,SACAC,EAASF,EAATE,UAASC,EAAAH,EACTI,UAAAA,OAAY,IAAHD,EAAG,IAAGA,EAAAE,EAAAL,EACfM,UAAAA,OAAY,IAAHD,EAAG,QAAOA,EAAAE,EAAAP,EACnBQ,SAAAA,OAAW,IAAHD,EAAG,eAAcA,EAAAE,EAAAT,EACzBU,OAAAA,OAAM,IAAAD,EAAG,CAAC,GAAI,KAAIA,EAAAE,EAAAX,EAClBY,MAAAA,OAAK,IAAAD,EAAG,OAAMA,EACdE,EAAYb,EAAZa,aAAYC,EAAAd,EACZe,WAAAA,OAAU,IAAAD,GAAQA,EAElBE,EAA8BC,EAAAA,UAAS,GAAhCC,EAAOF,EAAA,GAAEG,EAAUH,EAAA,GACpBI,EAASC,EAAAA,OAAuB,MAC/BC,EAAWC,EAAAA,WAGf,CACDC,WAAYN,EACZO,sBAAuB,OALX,GAQRC,EAAcC,EAAcA,eAACd,GAA8BO,EAAQ,CACvEL,WAAAA,EACAa,UAAU,EACVC,aAAc,MAGVC,EAAgBC,EAAOA,QAAC,WAAK,IAAAC,EACjCC,EAAezB,EAAS0B,MAAM,KAApBC,EAACF,EACX,GAAA,OAAAD,EAAA,CAAA,GADQC,EAAEE,IAEAzB,EAAO,GAAEsB,KAAAA,EAChBG,GAAOzB,EAAO,GAAE,KAAAsB,CAErB,EAAG,CAACxB,EAAUE,IA6Bd,OA3BA0B,EAASA,UAAC,WAER,GADAC,QAAQC,IAAI,cAAeZ,GACvBA,GAAeA,EAAYa,SAAU,CACV,MAA7BjB,EAAQG,uBAARH,EAAQG,wBACR,IAAMe,EAAW,WACf,IAAQC,EAAoBf,EAApBe,gBACFC,KACJD,GACCA,EAAgCrC,UAAYA,GAE3CkB,EAAQE,aAAekB,IACzBpB,EAAQE,WAAakB,EACrBvB,EAAWuB,GAEf,EAOA,OANAF,IACAlB,EAAQG,sBAAwBkB,EAAgBA,iBAC9CjB,EAAYa,SACZ,SACAC,GAEK,WACLlB,MAAAA,EAAQG,uBAARH,EAAQG,uBACV,CACD,CACH,EAAG,CAACC,EAAYa,wBAGZK,EAAA,QAAAC,cAAA,MAAA,CACEjC,MAAOA,EACPkC,IAAK1B,EACL2B,MAAOjB,EACP5B,UAAW8C,EAAAA,WACTnD,EAAE,YAAa,CACbqB,UAAWA,EACX+B,WAAY/B,IAEdrB,EAAE,CAAEqD,SAAwB,aAAd5C,IACdJ,GAEFiD,QAAS,WAAK,IAAAC,EACZA,OAAAA,EAAA1B,EAAYa,WAAZa,EAAsBC,SAAS,CAC7BC,IAAK,EACLC,SAAU,UAEd,GAECtD,GAAY,IAGrB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import e,{useState as l,useRef as r,useMemo as o,useEffect as t}from"react";import{prefixClassname as s,classNames as i}from"@ohkit/prefix-classname";import{useRuntime as c,useScrollEntry as n}from"@ohkit/react-helper";import{addEventListener as m}from"@ohkit/dom-helper";const a=s("ohkit-back-top__"),p=({children:s,className:p,scrollTop:u=500,mountType:v="fixed",position:f="bottom-right",offset:b=[50,100],title:h="返回顶部",scrollRefDom:R,realScroll:k=!1})=>{const[L,d]=l(!1),x=r(null),[T]=c({preVisible:L,scrollListenerRemover:null}),y=n(R||x,{realScroll:k,autoFind:!0,intervalTime:3e3}),V=o(()=>{const[e,l]=f.split("-");return{[e]:`${b[0]}px`,[l]:`${b[1]}px`}},[f,b]);return t(()=>{if(console.log("scrollEntry",y),y&&y.scroller){null==T.scrollListenerRemover||T.scrollListenerRemover();const e=()=>{const{scrollContainer:e}=y,l=!!(e&&e.scrollTop>u);T.preVisible!==l&&(T.preVisible=l,d(l))};return e(),T.scrollListenerRemover=m(y.scroller,"scroll",e),()=>{null==T.scrollListenerRemover||T.scrollListenerRemover()}}},[y.scroller]),/*#__PURE__*/e.createElement("div",{title:h,ref:x,style:V,className:i(a("container",{visible:!!L,invisible:!L}),a({absolute:"absolute"===v}),p),onClick:()=>{var e;null==(e=y.scroller)||e.scrollTo({top:0,behavior:"smooth"})}},s||"↑")};export{p as BackTop,a as c};
|
|
2
|
+
//# sourceMappingURL=index.modern.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.modern.mjs","sources":["../src/index.tsx"],"sourcesContent":["import React, { PropsWithChildren, useEffect, useState, useMemo, useRef } from \"react\";\nimport {\n prefixClassname as p,\n classNames as cx,\n} from \"@ohkit/prefix-classname\";\nimport { useRuntime, useScrollEntry } from \"@ohkit/react-helper\";\nimport { addEventListener } from \"@ohkit/dom-helper\";\n\nimport \"./style.scss\";\n\nexport const c = p(\"ohkit-back-top__\");\n\nexport interface IBackTop {\n className?: string;\n /**\n * 按钮位置,fixed 或 absolute\n * @default \"fixed\"\n */\n mountType?: \"fixed\" | \"absolute\";\n /**\n * 滚动距离,当滚动距离大于此值时显示返回顶部按钮\n * @default 500\n */\n scrollTop?: number;\n scrollRefDom?: React.MutableRefObject<HTMLElement>;\n /**\n * 按钮位置,top-right 或 top-left 或 bottom-right 或 bottom-left\n * @default \"bottom-right\"\n */\n position?: \"top-right\" | \"top-left\" | \"bottom-right\" | \"bottom-left\";\n /**\n * 按钮位置偏移量px, 即[position.split(\"-\")[0], position.split(\"-\")[1]]\n * @default[50, 100]\n */\n offset?: [number, number];\n /**\n * 是否查找真实可滚动的容器,scrollHeight > clientHeight\n * @default false\n */\n realScroll?: boolean;\n /**\n * 按钮标题\n * @default \"返回顶部\"\n */\n title?: string;\n}\n\nexport type BackTopProps = PropsWithChildren<IBackTop>;\n\nexport const BackTop = ({\n children,\n className,\n scrollTop = 500,\n mountType = \"fixed\",\n position = \"bottom-right\",\n offset = [50, 100],\n title = \"返回顶部\",\n scrollRefDom,\n realScroll = false,\n}: BackTopProps) => {\n const [visible, setVisible] = useState(false);\n const domRef = useRef<HTMLDivElement>(null);\n const [runtime] = useRuntime<{\n preVisible: boolean;\n scrollListenerRemover: null | (() => void);\n }>({\n preVisible: visible,\n scrollListenerRemover: null,\n });\n\n const scrollEntry = useScrollEntry(scrollRefDom ? scrollRefDom : domRef, {\n realScroll,\n autoFind: true,\n intervalTime: 3000,\n });\n\n const positionStyle = useMemo(() => {\n const [x, y] = position.split(\"-\");\n return {\n [x]: `${offset[0]}px`,\n [y]: `${offset[1]}px`,\n };\n }, [position, offset]);\n\n useEffect(() => {\n console.log('scrollEntry', scrollEntry);\n if (scrollEntry && scrollEntry.scroller) {\n runtime.scrollListenerRemover?.();\n const onScroll = () => {\n const { scrollContainer } = scrollEntry;\n const curVisible = !!(\n scrollContainer &&\n (scrollContainer as HTMLElement).scrollTop > scrollTop\n );\n if (runtime.preVisible !== curVisible) {\n runtime.preVisible = curVisible;\n setVisible(curVisible);\n }\n };\n onScroll();\n runtime.scrollListenerRemover = addEventListener(\n scrollEntry.scroller,\n \"scroll\",\n onScroll\n );\n return () => {\n runtime.scrollListenerRemover?.();\n };\n }\n }, [scrollEntry.scroller]);\n\n return (\n <div\n title={title}\n ref={domRef}\n style={positionStyle}\n className={cx(\n c(\"container\", {\n visible: !!visible,\n invisible: !visible,\n }),\n c({ absolute: mountType === \"absolute\" }),\n className\n )}\n onClick={() => {\n scrollEntry.scroller?.scrollTo({\n top: 0,\n behavior: \"smooth\",\n });\n }}\n >\n {children || '↑'}\n </div>\n );\n};\n"],"names":["c","p","BackTop","children","className","scrollTop","mountType","position","offset","title","scrollRefDom","realScroll","visible","setVisible","useState","domRef","useRef","runtime","useRuntime","preVisible","scrollListenerRemover","scrollEntry","useScrollEntry","autoFind","intervalTime","positionStyle","useMemo","x","y","split","useEffect","console","log","scroller","onScroll","scrollContainer","curVisible","addEventListener","React","createElement","ref","style","cx","invisible","absolute","onClick","_scrollEntry$scroller","scrollTo","top","behavior"],"mappings":"gRAUa,MAAAA,EAAIC,EAAE,oBAuCNC,EAAUA,EACrBC,WACAC,YACAC,UAAAA,EAAY,IACZC,UAAAA,EAAY,QACZC,SAAAA,EAAW,eACXC,OAAAA,EAAS,CAAC,GAAI,KACdC,MAAAA,EAAQ,OACRC,eACAC,WAAAA,GAAa,MAEb,MAAOC,EAASC,GAAcC,GAAS,GACjCC,EAASC,EAAuB,OAC/BC,GAAWC,EAGf,CACDC,WAAYP,EACZQ,sBAAuB,OAGnBC,EAAcC,EAAeZ,GAA8BK,EAAQ,CACvEJ,WAAAA,EACAY,UAAU,EACVC,aAAc,MAGVC,EAAgBC,EAAQ,KAC5B,MAAOC,EAAGC,GAAKrB,EAASsB,MAAM,KAC9B,MAAO,CACLF,CAACA,GAAI,GAAGnB,EAAO,OACfoB,CAACA,GAAI,GAAGpB,EAAO,SAEhB,CAACD,EAAUC,IA6Bd,OA3BAsB,EAAU,KAER,GADAC,QAAQC,IAAI,cAAeX,GACvBA,GAAeA,EAAYY,SAAU,CACvChB,MAAAA,EAAQG,uBAARH,EAAQG,wBACR,MAAMc,EAAWA,KACf,MAAMC,gBAAEA,GAAoBd,EACtBe,KACJD,GACCA,EAAgC9B,UAAYA,GAE3CY,EAAQE,aAAeiB,IACzBnB,EAAQE,WAAaiB,EACrBvB,EAAWuB,KASf,OANAF,IACAjB,EAAQG,sBAAwBiB,EAC9BhB,EAAYY,SACZ,SACAC,GAEK,KACLjB,MAAAA,EAAQG,uBAARH,EAAQG,wBAEX,GACA,CAACC,EAAYY,wBAGZK,EAAAC,cACE9B,MAAAA,CAAAA,MAAOA,EACP+B,IAAKzB,EACL0B,MAAOhB,EACPrB,UAAWsC,EACT1C,EAAE,YAAa,CACbY,UAAWA,EACX+B,WAAY/B,IAEdZ,EAAE,CAAE4C,SAAwB,aAAdtC,IACdF,GAEFyC,QAASA,KAAKC,IAAAA,EACZA,OAAAA,EAAAzB,EAAYY,WAAZa,EAAsBC,SAAS,CAC7BC,IAAK,EACLC,SAAU,aAIb9C,GAAY"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("react"),require("@ohkit/prefix-classname"),require("@ohkit/react-helper"),require("@ohkit/dom-helper")):"function"==typeof define&&define.amd?define(["exports","react","@ohkit/prefix-classname","@ohkit/react-helper","@ohkit/dom-helper"],r):r((e||self).backTop={},e.react,e.prefixClassname,e.reactHelper,e.domHelper)}(this,function(e,r,o,l,t){function i(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var n=/*#__PURE__*/i(r),s=o.prefixClassname("ohkit-back-top__");e.BackTop=function(e){var i=e.children,c=e.className,a=e.scrollTop,u=void 0===a?500:a,f=e.mountType,p=void 0===f?"fixed":f,d=e.position,m=void 0===d?"bottom-right":d,v=e.offset,h=void 0===v?[50,100]:v,b=e.title,k=void 0===b?"返回顶部":b,x=e.scrollRefDom,y=e.realScroll,R=void 0!==y&&y,T=r.useState(!1),L=T[0],E=T[1],g=r.useRef(null),q=l.useRuntime({preVisible:L,scrollListenerRemover:null})[0],C=l.useScrollEntry(x||g,{realScroll:R,autoFind:!0,intervalTime:3e3}),S=r.useMemo(function(){var e,r=m.split("-"),o=r[1];return(e={})[r[0]]=h[0]+"px",e[o]=h[1]+"px",e},[m,h]);return r.useEffect(function(){if(console.log("scrollEntry",C),C&&C.scroller){null==q.scrollListenerRemover||q.scrollListenerRemover();var e=function(){var e=C.scrollContainer,r=!!(e&&e.scrollTop>u);q.preVisible!==r&&(q.preVisible=r,E(r))};return e(),q.scrollListenerRemover=t.addEventListener(C.scroller,"scroll",e),function(){null==q.scrollListenerRemover||q.scrollListenerRemover()}}},[C.scroller]),/*#__PURE__*/n.default.createElement("div",{title:k,ref:g,style:S,className:o.classNames(s("container",{visible:!!L,invisible:!L}),s({absolute:"absolute"===p}),c),onClick:function(){var e;null==(e=C.scroller)||e.scrollTo({top:0,behavior:"smooth"})}},i||"↑")},e.c=s});
|
|
2
|
+
//# sourceMappingURL=index.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.umd.js","sources":["../src/index.tsx"],"sourcesContent":["import React, { PropsWithChildren, useEffect, useState, useMemo, useRef } from \"react\";\nimport {\n prefixClassname as p,\n classNames as cx,\n} from \"@ohkit/prefix-classname\";\nimport { useRuntime, useScrollEntry } from \"@ohkit/react-helper\";\nimport { addEventListener } from \"@ohkit/dom-helper\";\n\nimport \"./style.scss\";\n\nexport const c = p(\"ohkit-back-top__\");\n\nexport interface IBackTop {\n className?: string;\n /**\n * 按钮位置,fixed 或 absolute\n * @default \"fixed\"\n */\n mountType?: \"fixed\" | \"absolute\";\n /**\n * 滚动距离,当滚动距离大于此值时显示返回顶部按钮\n * @default 500\n */\n scrollTop?: number;\n scrollRefDom?: React.MutableRefObject<HTMLElement>;\n /**\n * 按钮位置,top-right 或 top-left 或 bottom-right 或 bottom-left\n * @default \"bottom-right\"\n */\n position?: \"top-right\" | \"top-left\" | \"bottom-right\" | \"bottom-left\";\n /**\n * 按钮位置偏移量px, 即[position.split(\"-\")[0], position.split(\"-\")[1]]\n * @default[50, 100]\n */\n offset?: [number, number];\n /**\n * 是否查找真实可滚动的容器,scrollHeight > clientHeight\n * @default false\n */\n realScroll?: boolean;\n /**\n * 按钮标题\n * @default \"返回顶部\"\n */\n title?: string;\n}\n\nexport type BackTopProps = PropsWithChildren<IBackTop>;\n\nexport const BackTop = ({\n children,\n className,\n scrollTop = 500,\n mountType = \"fixed\",\n position = \"bottom-right\",\n offset = [50, 100],\n title = \"返回顶部\",\n scrollRefDom,\n realScroll = false,\n}: BackTopProps) => {\n const [visible, setVisible] = useState(false);\n const domRef = useRef<HTMLDivElement>(null);\n const [runtime] = useRuntime<{\n preVisible: boolean;\n scrollListenerRemover: null | (() => void);\n }>({\n preVisible: visible,\n scrollListenerRemover: null,\n });\n\n const scrollEntry = useScrollEntry(scrollRefDom ? scrollRefDom : domRef, {\n realScroll,\n autoFind: true,\n intervalTime: 3000,\n });\n\n const positionStyle = useMemo(() => {\n const [x, y] = position.split(\"-\");\n return {\n [x]: `${offset[0]}px`,\n [y]: `${offset[1]}px`,\n };\n }, [position, offset]);\n\n useEffect(() => {\n console.log('scrollEntry', scrollEntry);\n if (scrollEntry && scrollEntry.scroller) {\n runtime.scrollListenerRemover?.();\n const onScroll = () => {\n const { scrollContainer } = scrollEntry;\n const curVisible = !!(\n scrollContainer &&\n (scrollContainer as HTMLElement).scrollTop > scrollTop\n );\n if (runtime.preVisible !== curVisible) {\n runtime.preVisible = curVisible;\n setVisible(curVisible);\n }\n };\n onScroll();\n runtime.scrollListenerRemover = addEventListener(\n scrollEntry.scroller,\n \"scroll\",\n onScroll\n );\n return () => {\n runtime.scrollListenerRemover?.();\n };\n }\n }, [scrollEntry.scroller]);\n\n return (\n <div\n title={title}\n ref={domRef}\n style={positionStyle}\n className={cx(\n c(\"container\", {\n visible: !!visible,\n invisible: !visible,\n }),\n c({ absolute: mountType === \"absolute\" }),\n className\n )}\n onClick={() => {\n scrollEntry.scroller?.scrollTo({\n top: 0,\n behavior: \"smooth\",\n });\n }}\n >\n {children || '↑'}\n </div>\n );\n};\n"],"names":["c","p","prefixClassname","_ref","children","className","_ref$scrollTop","scrollTop","_ref$mountType","mountType","_ref$position","position","_ref$offset","offset","_ref$title","title","scrollRefDom","_ref$realScroll","realScroll","_useState","useState","visible","setVisible","domRef","useRef","runtime","useRuntime","preVisible","scrollListenerRemover","scrollEntry","useScrollEntry","autoFind","intervalTime","positionStyle","useMemo","_ref2","_position$split","split","y","useEffect","console","log","scroller","onScroll","scrollContainer","curVisible","addEventListener","React","createElement","ref","style","cx","invisible","absolute","onClick","_scrollEntry$scroller","scrollTo","top","behavior"],"mappings":"yjBAUaA,EAAIC,EAACC,gBAAC,8BAuCI,SAAHC,GAUD,IATjBC,EAAQD,EAARC,SACAC,EAASF,EAATE,UAASC,EAAAH,EACTI,UAAAA,OAAY,IAAHD,EAAG,IAAGA,EAAAE,EAAAL,EACfM,UAAAA,OAAY,IAAHD,EAAG,QAAOA,EAAAE,EAAAP,EACnBQ,SAAAA,OAAW,IAAHD,EAAG,eAAcA,EAAAE,EAAAT,EACzBU,OAAAA,OAAM,IAAAD,EAAG,CAAC,GAAI,KAAIA,EAAAE,EAAAX,EAClBY,MAAAA,OAAK,IAAAD,EAAG,OAAMA,EACdE,EAAYb,EAAZa,aAAYC,EAAAd,EACZe,WAAAA,OAAU,IAAAD,GAAQA,EAElBE,EAA8BC,EAAAA,UAAS,GAAhCC,EAAOF,EAAA,GAAEG,EAAUH,EAAA,GACpBI,EAASC,EAAAA,OAAuB,MAC/BC,EAAWC,EAAAA,WAGf,CACDC,WAAYN,EACZO,sBAAuB,OALX,GAQRC,EAAcC,EAAcA,eAACd,GAA8BO,EAAQ,CACvEL,WAAAA,EACAa,UAAU,EACVC,aAAc,MAGVC,EAAgBC,EAAOA,QAAC,WAAK,IAAAC,EACjCC,EAAezB,EAAS0B,MAAM,KAApBC,EAACF,EACX,GAAA,OAAAD,EAAA,CAAA,GADQC,EAAEE,IAEAzB,EAAO,GAAEsB,KAAAA,EAChBG,GAAOzB,EAAO,GAAE,KAAAsB,CAErB,EAAG,CAACxB,EAAUE,IA6Bd,OA3BA0B,EAASA,UAAC,WAER,GADAC,QAAQC,IAAI,cAAeZ,GACvBA,GAAeA,EAAYa,SAAU,CACV,MAA7BjB,EAAQG,uBAARH,EAAQG,wBACR,IAAMe,EAAW,WACf,IAAQC,EAAoBf,EAApBe,gBACFC,KACJD,GACCA,EAAgCrC,UAAYA,GAE3CkB,EAAQE,aAAekB,IACzBpB,EAAQE,WAAakB,EACrBvB,EAAWuB,GAEf,EAOA,OANAF,IACAlB,EAAQG,sBAAwBkB,EAAgBA,iBAC9CjB,EAAYa,SACZ,SACAC,GAEK,WACLlB,MAAAA,EAAQG,uBAARH,EAAQG,uBACV,CACD,CACH,EAAG,CAACC,EAAYa,wBAGZK,EAAA,QAAAC,cAAA,MAAA,CACEjC,MAAOA,EACPkC,IAAK1B,EACL2B,MAAOjB,EACP5B,UAAW8C,EAAAA,WACTnD,EAAE,YAAa,CACbqB,UAAWA,EACX+B,WAAY/B,IAEdrB,EAAE,CAAEqD,SAAwB,aAAd5C,IACdJ,GAEFiD,QAAS,WAAK,IAAAC,EACZA,OAAAA,EAAA1B,EAAYa,WAAZa,EAAsBC,SAAS,CAC7BC,IAAK,EACLC,SAAU,UAEd,GAECtD,GAAY,IAGrB"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React, { PropsWithChildren } from "react";
|
|
2
|
+
import { classNames as cx } from "@ohkit/prefix-classname";
|
|
3
|
+
import "./style.scss";
|
|
4
|
+
export declare const c: (...arg: cx.ArgumentArray) => string;
|
|
5
|
+
export interface IBackTop {
|
|
6
|
+
className?: string;
|
|
7
|
+
/**
|
|
8
|
+
* 按钮位置,fixed 或 absolute
|
|
9
|
+
* @default "fixed"
|
|
10
|
+
*/
|
|
11
|
+
mountType?: "fixed" | "absolute";
|
|
12
|
+
/**
|
|
13
|
+
* 滚动距离,当滚动距离大于此值时显示返回顶部按钮
|
|
14
|
+
* @default 500
|
|
15
|
+
*/
|
|
16
|
+
scrollTop?: number;
|
|
17
|
+
scrollRefDom?: React.MutableRefObject<HTMLElement>;
|
|
18
|
+
/**
|
|
19
|
+
* 按钮位置,top-right 或 top-left 或 bottom-right 或 bottom-left
|
|
20
|
+
* @default "bottom-right"
|
|
21
|
+
*/
|
|
22
|
+
position?: "top-right" | "top-left" | "bottom-right" | "bottom-left";
|
|
23
|
+
/**
|
|
24
|
+
* 按钮位置偏移量px, 即[position.split("-")[0], position.split("-")[1]]
|
|
25
|
+
* @default[50, 100]
|
|
26
|
+
*/
|
|
27
|
+
offset?: [number, number];
|
|
28
|
+
/**
|
|
29
|
+
* 是否查找真实可滚动的容器,scrollHeight > clientHeight
|
|
30
|
+
* @default false
|
|
31
|
+
*/
|
|
32
|
+
realScroll?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* 按钮标题
|
|
35
|
+
* @default "返回顶部"
|
|
36
|
+
*/
|
|
37
|
+
title?: string;
|
|
38
|
+
}
|
|
39
|
+
export type BackTopProps = PropsWithChildren<IBackTop>;
|
|
40
|
+
export declare const BackTop: ({ children, className, scrollTop, mountType, position, offset, title, scrollRefDom, realScroll, }: BackTopProps) => React.JSX.Element;
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ohkit/back-top",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "列表滚动超过指定距离后自动展示返回顶部按钮",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"back-top"
|
|
7
|
+
],
|
|
8
|
+
"homepage": "https://wuqiuyang.github.io/ohkit/storybook-static",
|
|
9
|
+
"license": "ISC",
|
|
10
|
+
"source": "src/index.@(js|jsx|mjs|ts|tsx)",
|
|
11
|
+
"main": "dist/index.js",
|
|
12
|
+
"umd:main": "dist/index.umd.js",
|
|
13
|
+
"module": "dist/index.es.js",
|
|
14
|
+
"types": "dist/types/index.d.ts",
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"registry": "https://registry.npmjs.org/",
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/WuQiuYang/ohkit"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "npm run clean && microbundle --jsx React.createElement",
|
|
25
|
+
"dev": "npm run watch",
|
|
26
|
+
"watch": "microbundle watch --jsx React.createElement --no-compress --format es --output dist/index.es.js",
|
|
27
|
+
"clean": "rm -rf dist"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@ohkit/dom-helper": "0.0.5",
|
|
31
|
+
"@ohkit/prefix-classname": "^0.0.3",
|
|
32
|
+
"@ohkit/react-helper": "0.0.5"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"react": ">=17",
|
|
36
|
+
"react-dom": ">=17"
|
|
37
|
+
},
|
|
38
|
+
"gitHead": "032c94e624cb5be095bae2032083c12a75230de6"
|
|
39
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import React, { PropsWithChildren, useEffect, useState, useMemo, useRef } from "react";
|
|
2
|
+
import {
|
|
3
|
+
prefixClassname as p,
|
|
4
|
+
classNames as cx,
|
|
5
|
+
} from "@ohkit/prefix-classname";
|
|
6
|
+
import { useRuntime, useScrollEntry } from "@ohkit/react-helper";
|
|
7
|
+
import { addEventListener } from "@ohkit/dom-helper";
|
|
8
|
+
|
|
9
|
+
import "./style.scss";
|
|
10
|
+
|
|
11
|
+
export const c = p("ohkit-back-top__");
|
|
12
|
+
|
|
13
|
+
export interface IBackTop {
|
|
14
|
+
className?: string;
|
|
15
|
+
/**
|
|
16
|
+
* 按钮位置,fixed 或 absolute
|
|
17
|
+
* @default "fixed"
|
|
18
|
+
*/
|
|
19
|
+
mountType?: "fixed" | "absolute";
|
|
20
|
+
/**
|
|
21
|
+
* 滚动距离,当滚动距离大于此值时显示返回顶部按钮
|
|
22
|
+
* @default 500
|
|
23
|
+
*/
|
|
24
|
+
scrollTop?: number;
|
|
25
|
+
scrollRefDom?: React.MutableRefObject<HTMLElement>;
|
|
26
|
+
/**
|
|
27
|
+
* 按钮位置,top-right 或 top-left 或 bottom-right 或 bottom-left
|
|
28
|
+
* @default "bottom-right"
|
|
29
|
+
*/
|
|
30
|
+
position?: "top-right" | "top-left" | "bottom-right" | "bottom-left";
|
|
31
|
+
/**
|
|
32
|
+
* 按钮位置偏移量px, 即[position.split("-")[0], position.split("-")[1]]
|
|
33
|
+
* @default[50, 100]
|
|
34
|
+
*/
|
|
35
|
+
offset?: [number, number];
|
|
36
|
+
/**
|
|
37
|
+
* 是否查找真实可滚动的容器,scrollHeight > clientHeight
|
|
38
|
+
* @default false
|
|
39
|
+
*/
|
|
40
|
+
realScroll?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* 按钮标题
|
|
43
|
+
* @default "返回顶部"
|
|
44
|
+
*/
|
|
45
|
+
title?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type BackTopProps = PropsWithChildren<IBackTop>;
|
|
49
|
+
|
|
50
|
+
export const BackTop = ({
|
|
51
|
+
children,
|
|
52
|
+
className,
|
|
53
|
+
scrollTop = 500,
|
|
54
|
+
mountType = "fixed",
|
|
55
|
+
position = "bottom-right",
|
|
56
|
+
offset = [50, 100],
|
|
57
|
+
title = "返回顶部",
|
|
58
|
+
scrollRefDom,
|
|
59
|
+
realScroll = false,
|
|
60
|
+
}: BackTopProps) => {
|
|
61
|
+
const [visible, setVisible] = useState(false);
|
|
62
|
+
const domRef = useRef<HTMLDivElement>(null);
|
|
63
|
+
const [runtime] = useRuntime<{
|
|
64
|
+
preVisible: boolean;
|
|
65
|
+
scrollListenerRemover: null | (() => void);
|
|
66
|
+
}>({
|
|
67
|
+
preVisible: visible,
|
|
68
|
+
scrollListenerRemover: null,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const scrollEntry = useScrollEntry(scrollRefDom ? scrollRefDom : domRef, {
|
|
72
|
+
realScroll,
|
|
73
|
+
autoFind: true,
|
|
74
|
+
intervalTime: 3000,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const positionStyle = useMemo(() => {
|
|
78
|
+
const [x, y] = position.split("-");
|
|
79
|
+
return {
|
|
80
|
+
[x]: `${offset[0]}px`,
|
|
81
|
+
[y]: `${offset[1]}px`,
|
|
82
|
+
};
|
|
83
|
+
}, [position, offset]);
|
|
84
|
+
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
console.log('scrollEntry', scrollEntry);
|
|
87
|
+
if (scrollEntry && scrollEntry.scroller) {
|
|
88
|
+
runtime.scrollListenerRemover?.();
|
|
89
|
+
const onScroll = () => {
|
|
90
|
+
const { scrollContainer } = scrollEntry;
|
|
91
|
+
const curVisible = !!(
|
|
92
|
+
scrollContainer &&
|
|
93
|
+
(scrollContainer as HTMLElement).scrollTop > scrollTop
|
|
94
|
+
);
|
|
95
|
+
if (runtime.preVisible !== curVisible) {
|
|
96
|
+
runtime.preVisible = curVisible;
|
|
97
|
+
setVisible(curVisible);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
onScroll();
|
|
101
|
+
runtime.scrollListenerRemover = addEventListener(
|
|
102
|
+
scrollEntry.scroller,
|
|
103
|
+
"scroll",
|
|
104
|
+
onScroll
|
|
105
|
+
);
|
|
106
|
+
return () => {
|
|
107
|
+
runtime.scrollListenerRemover?.();
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}, [scrollEntry.scroller]);
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div
|
|
114
|
+
title={title}
|
|
115
|
+
ref={domRef}
|
|
116
|
+
style={positionStyle}
|
|
117
|
+
className={cx(
|
|
118
|
+
c("container", {
|
|
119
|
+
visible: !!visible,
|
|
120
|
+
invisible: !visible,
|
|
121
|
+
}),
|
|
122
|
+
c({ absolute: mountType === "absolute" }),
|
|
123
|
+
className
|
|
124
|
+
)}
|
|
125
|
+
onClick={() => {
|
|
126
|
+
scrollEntry.scroller?.scrollTo({
|
|
127
|
+
top: 0,
|
|
128
|
+
behavior: "smooth",
|
|
129
|
+
});
|
|
130
|
+
}}
|
|
131
|
+
>
|
|
132
|
+
{children || '↑'}
|
|
133
|
+
</div>
|
|
134
|
+
);
|
|
135
|
+
};
|
package/src/style.scss
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
$prefix: ohkit;
|
|
2
|
+
|
|
3
|
+
.#{$prefix}-back-top__ {
|
|
4
|
+
&container {
|
|
5
|
+
// 添加你的样式
|
|
6
|
+
position: fixed;
|
|
7
|
+
// right: 100px;
|
|
8
|
+
// bottom: 50px;
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
padding: 4px;
|
|
11
|
+
min-width: 40px;
|
|
12
|
+
min-height: 40px;
|
|
13
|
+
line-height: 1.2em;
|
|
14
|
+
z-index: 10;
|
|
15
|
+
display: flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
justify-content: center;
|
|
18
|
+
font-size: 14px;
|
|
19
|
+
color: #fff;
|
|
20
|
+
white-space: pre-line;
|
|
21
|
+
border-radius: 4px;
|
|
22
|
+
// border-radius: 50%;
|
|
23
|
+
|
|
24
|
+
transition: all 0.2s ease-in;
|
|
25
|
+
}
|
|
26
|
+
&absolute {
|
|
27
|
+
position: absolute;
|
|
28
|
+
right: 20px;
|
|
29
|
+
bottom: 20px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&invisible {
|
|
33
|
+
pointer-events: none;
|
|
34
|
+
opacity: 0;
|
|
35
|
+
background: transparent;
|
|
36
|
+
}
|
|
37
|
+
&visible {
|
|
38
|
+
background: rgba(78, 85, 98, 0.25);
|
|
39
|
+
opacity: 1;
|
|
40
|
+
cursor: pointer;
|
|
41
|
+
|
|
42
|
+
&:hover {
|
|
43
|
+
background: rgba(78, 85, 98, 0.4);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|