From f69b2cf92e39eb10080410436ba77378a28efcd6 Mon Sep 17 00:00:00 2001 From: Tang <1194593491@qq.com> Date: Wed, 3 Jul 2024 19:43:56 +0800 Subject: [PATCH 1/9] =?UTF-8?q?feat(back-top):=20back-top=20complete?= =?UTF-8?q?=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/sidebar.config.ts | 6 + src/_common | 2 +- src/back-top/README.md | 59 ++++++++++ src/back-top/_example/base.tsx | 37 ++++++ src/back-top/_example/baseCustom.tsx | 23 ++++ src/back-top/_example/baseDuration.tsx | 38 +++++++ src/back-top/_example/baseShape.tsx | 62 ++++++++++ src/back-top/_example/baseSize.tsx | 70 ++++++++++++ src/back-top/_example/baseTheme.tsx | 52 +++++++++ src/back-top/back-top.tsx | 152 +++++++++++++++++++++++++ src/back-top/index.ts | 5 + src/back-top/style/css.js | 1 + src/back-top/style/index.ts | 10 ++ src/back-top/type.ts | 75 ++++++++++++ src/index.ts | 1 + 15 files changed, 592 insertions(+), 1 deletion(-) create mode 100644 src/back-top/README.md create mode 100644 src/back-top/_example/base.tsx create mode 100644 src/back-top/_example/baseCustom.tsx create mode 100644 src/back-top/_example/baseDuration.tsx create mode 100644 src/back-top/_example/baseShape.tsx create mode 100644 src/back-top/_example/baseSize.tsx create mode 100644 src/back-top/_example/baseTheme.tsx create mode 100644 src/back-top/back-top.tsx create mode 100644 src/back-top/index.ts create mode 100644 src/back-top/style/css.js create mode 100644 src/back-top/style/index.ts create mode 100644 src/back-top/type.ts diff --git a/site/sidebar.config.ts b/site/sidebar.config.ts index 4b4e85b..712579f 100644 --- a/site/sidebar.config.ts +++ b/site/sidebar.config.ts @@ -92,6 +92,12 @@ export default [ path: '/components/breadcrumb', // component: () => import('tdesign-web-components/breadcrumb/README.md'), }, + { + title: 'BackTop 回到顶部', + name: 'backTop', + path: '/components/backTop', + component: () => import('tdesign-web-components/back-top/README.md'), + }, ], }, { diff --git a/src/_common b/src/_common index a3e26cf..4954259 160000 --- a/src/_common +++ b/src/_common @@ -1 +1 @@ -Subproject commit a3e26cf8ce5053ac790b6dcdaee7190cc39a4281 +Subproject commit 495425961c7b23dc8a38f8f0f841ea6487a9adc2 diff --git a/src/back-top/README.md b/src/back-top/README.md new file mode 100644 index 0000000..c0c0ac1 --- /dev/null +++ b/src/back-top/README.md @@ -0,0 +1,59 @@ +--- +title: BackTop 回到顶部 +description: 用于返回页面顶部 +isComponent: true +usage: { title: '', description: '' } +spline: base +--- + +### 基础的回到顶部 + +默认距离页面右侧24px,距离页面底部80px,滚动动画时长200ms。 +{{ base }} + +### 不同耗时的回到顶部 + +设置滚动动画时长2s。 +{{ baseDuration }} + +### 不同组件尺寸的回到顶部 + +提供标准(默认)、小两种尺寸。 +{{ baseSize }} + +### 不同组件形状的回到顶部 + +提供方形(默认)、圆形两种形状。 +{{ baseShape }} + +### 不同组件主题的回到顶部 + +{{ baseTheme }} + +### 可自定义内容的回到顶部 + +{{ baseCustom }} + +## API + +[通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) + +| 名称 | 类型 | 默认值 | 说明 | 必传 | +|---------------|-------------------|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----| +| className | String | - | 类名 | N | +| style | Object | - | 样式,TS 类型:`Record` | N | +| children | TNode | - | 回到顶部内容,同 `content`。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) | N | +| container | String / Function | 'body' | 监听滚动的容器。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body。TS 类型:AttachNode。 [通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) | N | +| content | TNode | - | 回到顶部内容。TS 类型:`string\| TNode`。 [通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) | N | +| default | TNode | - | 回到顶部内容,同 content。TS 类型:`string\| TNode`。 [通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) | N | +| duration | Number | 200 | 回到顶部的耗时单位:毫秒 | N | +| offset | Array | ["24px", "80px"] | 回到顶部相对右下角的位置偏移,示例:[10, 20] 或 ['10em', '8rem']。TS 类型:`Array` | N | +| shape | String | square | 回到顶部的形状。可选项:circle/square。TS 类型:`BackTopShapeEnum type BackTopShapeEnum = 'circle' \| 'square'`。 | N | +| size | String | medium | 组件尺寸。可选项:medium/small | N | +| target | String / Function | 'body' | 指定回到该对象。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body。TS 类型:AttachNode。[通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) | N | +| theme | String | light | 组件主题风格,浅色、主色、深色。可选项:light/primary/dark | N | +| visibleHeight | String / Number | '200px' | 滚动高度达到此参数值才出现 | N | +| onClick | Function | - | TS 类型:`(context: { e: MouseEvent }) => void`
点击回到顶部时触发 | N | + + + diff --git a/src/back-top/_example/base.tsx b/src/back-top/_example/base.tsx new file mode 100644 index 0000000..7a1f16e --- /dev/null +++ b/src/back-top/_example/base.tsx @@ -0,0 +1,37 @@ +import 'tdesign-web-components/back-top'; + +import { Component, createRef } from 'omi'; + +export default class BackTop extends Component { + render() { + const container = createRef(); + return ( +
+
+
    + {Array.from(Array(50), () => '列表内容').map((item, index) => ( +
  • {item}
  • + ))} +
+
+ container.current} + style={{ position: 'absolute' }} + visibleHeight={46} + shape="square" + offset={['24px', '80px']} + ignoreAttributes={['style']} + /> +
+ ); + } +} diff --git a/src/back-top/_example/baseCustom.tsx b/src/back-top/_example/baseCustom.tsx new file mode 100644 index 0000000..509d721 --- /dev/null +++ b/src/back-top/_example/baseCustom.tsx @@ -0,0 +1,23 @@ +import 'tdesign-web-components/space'; +import 'tdesign-web-components/back-top'; + +import { Component } from 'omi'; + +export default class BackTop extends Component { + render() { + const style = { + position: 'relative', + insetInlineEnd: 0, + insetBlockEnd: 0, + }; + return ( + + document}> + 返回 + + TOP} visibleHeight={0} container={() => document} /> + UP} visibleHeight={0} container={() => document} /> + + ); + } +} diff --git a/src/back-top/_example/baseDuration.tsx b/src/back-top/_example/baseDuration.tsx new file mode 100644 index 0000000..0d0a528 --- /dev/null +++ b/src/back-top/_example/baseDuration.tsx @@ -0,0 +1,38 @@ +import 'tdesign-web-components/back-top'; + +import { Component, createRef } from 'omi'; + +export default class BackTop extends Component { + render() { + const container = createRef(); + return ( +
+
+
    + {Array.from(Array(50), () => '列表内容').map((item, index) => ( +
  • {item}
  • + ))} +
+
+ container.current} + style={{ position: 'absolute' }} + visibleHeight={46} + shape="square" + duration={2000} + offset={['24px', '80px']} + ignoreAttributes={['style']} + /> +
+ ); + } +} diff --git a/src/back-top/_example/baseShape.tsx b/src/back-top/_example/baseShape.tsx new file mode 100644 index 0000000..ec88250 --- /dev/null +++ b/src/back-top/_example/baseShape.tsx @@ -0,0 +1,62 @@ +import 'tdesign-web-components/space'; +import 'tdesign-web-components/back-top'; + +import { Component } from 'omi'; + +export default class BackTop extends Component { + render() { + const style = { + position: 'relative', + insetInlineEnd: 0, + insetBlockEnd: 0, + }; + return ( + + + document} + /> + document} /> + + + document} + /> + document} + /> + + + document} + /> + document} + /> + + + ); + } +} diff --git a/src/back-top/_example/baseSize.tsx b/src/back-top/_example/baseSize.tsx new file mode 100644 index 0000000..1ec9e0a --- /dev/null +++ b/src/back-top/_example/baseSize.tsx @@ -0,0 +1,70 @@ +import 'tdesign-web-components/space'; +import 'tdesign-web-components/back-top'; + +import { Component } from 'omi'; + +export default class BackTop extends Component { + render() { + const style = { + position: 'relative', + insetInlineEnd: 0, + insetBlockEnd: 0, + }; + return ( + + + document} + /> + document} + /> + + + document} + /> + document} + /> + + + document} + /> + document} + /> + + + ); + } +} diff --git a/src/back-top/_example/baseTheme.tsx b/src/back-top/_example/baseTheme.tsx new file mode 100644 index 0000000..c51e309 --- /dev/null +++ b/src/back-top/_example/baseTheme.tsx @@ -0,0 +1,52 @@ +import 'tdesign-web-components/space'; +import 'tdesign-web-components/back-top'; + +import { Component } from 'omi'; + +export default class BackTop extends Component { + render() { + const style = { + position: 'relative', + insetInlineEnd: 0, + insetBlockEnd: 0, + }; + return ( + + + document} /> + document} /> + document} /> + + + document} /> + document} /> + document} /> + + + document} /> + document} /> + document} /> + + + document} /> + document} + /> + document} + /> + + + ); + } +} diff --git a/src/back-top/back-top.tsx b/src/back-top/back-top.tsx new file mode 100644 index 0000000..779cff5 --- /dev/null +++ b/src/back-top/back-top.tsx @@ -0,0 +1,152 @@ +import 'tdesign-web-components/icon'; + +import { Component, computed, createRef, effect, OmiProps, signal, SignalValue, tag } from 'omi'; + +import classname, { getClassPrefix } from '../_util/classname'; +import { scrollTo } from '../_util/dom.ts'; +import { StyledProps } from '../common'; +import { styleSheet } from './style'; +import { TdBackTopProps } from './type.ts'; + +type Element = HTMLElement | Window | Document; + +export interface BackTopProps extends TdBackTopProps, StyledProps {} + +const getContainer = (container: string | Function) => { + if (typeof container === 'string') { + if (typeof document !== 'undefined') { + return document.querySelector(container); + } + } + if (typeof container === 'function') { + return container(); + } + return null; +}; + +@tag('t-back-top') +export default class BackTop extends Component { + static css = styleSheet; + + buttonRef: Partial> = createRef(); + + containerRef: Partial> = createRef(); + + visible: SignalValue = signal(false); + + needUninstall: Set = new Set(); + + static defaultProps = { + container: 'body', + duration: 200, + offset: ['24px', '80px'], + shape: 'square', + size: 'medium', + target: 'body', + theme: 'light', + visibleHeight: '200px', + }; + + ready() { + const { container, visibleHeight } = this.props; + const scrollTop = signal(0); + const scrollContainer = getContainer(container); + this.containerRef.current = scrollContainer; + const updateScrollTop = () => { + if (scrollContainer === document) { + scrollTop.value = scrollContainer.documentElement.scrollTop; + } else { + scrollTop.value = scrollContainer.scrollTop; + } + }; + const updateVisible = () => { + if (typeof visibleHeight === 'string') { + this.visible.value = scrollTop.value >= Number(visibleHeight.replace('px', '')); + return; + } + this.visible.value = scrollTop.value >= visibleHeight; + }; + scrollContainer.addEventListener('scroll', updateScrollTop); + const scrollListenerUninstall = () => { + scrollContainer.removeEventListener('scroll', updateScrollTop); + }; + const visibleUninstall: Function = effect(updateVisible); + + this.needUninstall.add(scrollListenerUninstall); + this.needUninstall.add(visibleUninstall); + } + + uninstall(): void { + this.needUninstall.forEach((uninstall) => { + uninstall(); + }); + } + + render(props: OmiProps) { + const { + theme, + size, + shape, + target, + container, + duration, + content, + offset, + children, + default: cusContent, + className, + style, + onClick, + ignoreAttributes, + } = props; + if (ignoreAttributes?.length > 0) { + ignoreAttributes.forEach((attr) => { + this.removeAttribute(attr); + }); + } + const backTopStyle: SignalValue = computed(() => ({ + insetInlineEnd: offset[0], + insetBlockEnd: offset[1], + ...style, + })); + const classPrefix = getClassPrefix(); + const cls = computed(() => + classname( + `${classPrefix}-back-top`, + `${classPrefix}-back-top--theme-${theme}`, + `${classPrefix}-back-top--${shape}`, + { + [`${classPrefix}-back-top--show`]: this.visible.value, + [`${classPrefix}-size-s`]: size === 'small', + [`${classPrefix}-size-m`]: size === 'medium', + }, + className, + ), + ); + const defaultContent = ( + <> + + TOP + + ); + const renderChildren = children || content || cusContent || defaultContent; + const getBackTo = (): number => { + if (target === container) return 0; + if (target === 'body') return 0; + if (!target) return 0; + const targetElement = getContainer(target); + if (!targetElement) return 0; + return (targetElement as HTMLElement).getBoundingClientRect().y; + }; + const handleClick = (e: MouseEvent) => { + const y = getBackTo(); + scrollTo(y, { container: this.containerRef.current, duration }); + onClick?.({ e }); + }; + return ( + + ); + } +} diff --git a/src/back-top/index.ts b/src/back-top/index.ts new file mode 100644 index 0000000..7573106 --- /dev/null +++ b/src/back-top/index.ts @@ -0,0 +1,5 @@ +import _BackTop from './back-top'; + +export type { BackTopProps } from './back-top'; +export const BackTop = _BackTop; +export default BackTop; diff --git a/src/back-top/style/css.js b/src/back-top/style/css.js new file mode 100644 index 0000000..6a9a4b1 --- /dev/null +++ b/src/back-top/style/css.js @@ -0,0 +1 @@ +import './index.css'; diff --git a/src/back-top/style/index.ts b/src/back-top/style/index.ts new file mode 100644 index 0000000..5c5f298 --- /dev/null +++ b/src/back-top/style/index.ts @@ -0,0 +1,10 @@ +import { css, globalCSS } from 'omi'; + +import backTopStyle from '../../_common/style/web/components/back-top/_index.less'; +import theme from '../../_common/style/web/theme/_index.less'; + +export const styleSheet = css` + ${backTopStyle} + ${theme} +`; + +globalCSS(styleSheet); diff --git a/src/back-top/type.ts b/src/back-top/type.ts new file mode 100644 index 0000000..141bffb --- /dev/null +++ b/src/back-top/type.ts @@ -0,0 +1,75 @@ +import { AttachNode, Styles, TNode } from '../common'; + +export interface TdBackTopProps { + /** + * 类名 + */ + className?: string; + /** + * 样式 + */ + style?: Styles; + /** + * 回到顶部的内容 + */ + children?: TNode; + /** + * 监听滚动的容器。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body + * @default 'body' + */ + container?: string | Function; + /** + * 回到顶部内容 + */ + content?: TNode; + /** + * 回到顶部内容,同 `content` + */ + default?: TNode; + /** + * 回到顶部的耗时单位:毫秒 + * @default 200 + */ + duration?: number; + /** + * 回到顶部相对右下角的位置偏移,示例:[10, 20] 或 ['10em', '8rem'] + * @default ["24px", "80px"] + */ + offset?: Array; + /** + * 回到顶部的形状 + * @default square + */ + shape?: BackTopShapeEnum; + /** + * 组件尺寸 + * @default medium + */ + size?: 'medium' | 'small'; + /** + * 指定回到该对象。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body + * @default 'body' + */ + target?: AttachNode; + /** + * 组件主题风格,浅色、主色、暗色 + * @default light + */ + theme?: 'light' | 'primary' | 'dark'; + /** + * 滚动高度达到此参数值才出现 + * @default '200px' + */ + visibleHeight?: string | number; + /** + * 点击回到顶部时触发 + */ + onClick?: (context: { e: MouseEvent }) => void; + /** + * 在host标签上忽略的属性 + * @default [] + */ + ignoreAttributes?: string[]; +} + +export type BackTopShapeEnum = 'circle' | 'square'; diff --git a/src/index.ts b/src/index.ts index 4ac56a9..c880a66 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ export * from './avatar'; +export * from './back-top'; export * from './button'; export * from './collapse'; export * from './common'; From f267c2b46aa90c5c96aea7d82c690d2a9e087efe Mon Sep 17 00:00:00 2001 From: Tang <1194593491@qq.com> Date: Thu, 4 Jul 2024 14:56:28 +0800 Subject: [PATCH 2/9] docs(back-top): update back-top's docs --- src/back-top/README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/back-top/README.md b/src/back-top/README.md index c0c0ac1..d2493a9 100644 --- a/src/back-top/README.md +++ b/src/back-top/README.md @@ -38,22 +38,22 @@ spline: base [通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) -| 名称 | 类型 | 默认值 | 说明 | 必传 | -|---------------|-------------------|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----| -| className | String | - | 类名 | N | -| style | Object | - | 样式,TS 类型:`Record` | N | -| children | TNode | - | 回到顶部内容,同 `content`。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) | N | -| container | String / Function | 'body' | 监听滚动的容器。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body。TS 类型:AttachNode。 [通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) | N | -| content | TNode | - | 回到顶部内容。TS 类型:`string\| TNode`。 [通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) | N | -| default | TNode | - | 回到顶部内容,同 content。TS 类型:`string\| TNode`。 [通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) | N | -| duration | Number | 200 | 回到顶部的耗时单位:毫秒 | N | -| offset | Array | ["24px", "80px"] | 回到顶部相对右下角的位置偏移,示例:[10, 20] 或 ['10em', '8rem']。TS 类型:`Array` | N | -| shape | String | square | 回到顶部的形状。可选项:circle/square。TS 类型:`BackTopShapeEnum type BackTopShapeEnum = 'circle' \| 'square'`。 | N | -| size | String | medium | 组件尺寸。可选项:medium/small | N | -| target | String / Function | 'body' | 指定回到该对象。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body。TS 类型:AttachNode。[通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) | N | -| theme | String | light | 组件主题风格,浅色、主色、深色。可选项:light/primary/dark | N | -| visibleHeight | String / Number | '200px' | 滚动高度达到此参数值才出现 | N | -| onClick | Function | - | TS 类型:`(context: { e: MouseEvent }) => void`
点击回到顶部时触发 | N | - +| 名称 | 类型 | 默认值 | 说明 | 必传 | +|------------------|-------------------|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----| +| className | String | - | 类名 | N | +| style | Object | - | 样式,TS 类型:`Record` | N | +| children | TNode | - | 回到顶部内容,同 `content`。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) | N | +| container | String / Function | 'body' | 监听滚动的容器。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body。TS 类型:AttachNode。 [通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) | N | +| content | TNode | - | 回到顶部内容。TS 类型:`string\| TNode`。 [通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) | N | +| default | TNode | - | 回到顶部内容,同 content。TS 类型:`string\| TNode`。 [通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) | N | +| duration | Number | 200 | 回到顶部的耗时单位:毫秒 | N | +| offset | Array | ["24px", "80px"] | 回到顶部相对右下角的位置偏移,示例:[10, 20] 或 ['10em', '8rem']。TS 类型:`Array` | N | +| shape | String | square | 回到顶部的形状。可选项:circle/square。TS 类型:`BackTopShapeEnum type BackTopShapeEnum = 'circle' \| 'square'`。 | N | +| size | String | medium | 组件尺寸。可选项:medium/small | N | +| target | String / Function | 'body' | 指定回到该对象。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body。TS 类型:AttachNode。[通用类型定义](https://github.com/TDesignOteam/tdesign-web-components/blob/main/src/common.ts) | N | +| theme | String | light | 组件主题风格,浅色、主色、深色。可选项:light/primary/dark | N | +| visibleHeight | String / Number | '200px' | 滚动高度达到此参数值才出现 | N | +| onClick | Function | - | TS 类型:`(context: { e: MouseEvent }) => void`
点击回到顶部时触发 | N | +| ignoreAttributes | String[] | - | 在host标签上忽略的属性 | N | From d3c2a01028a2d1746b60e7df939a05791736cf16 Mon Sep 17 00:00:00 2001 From: Tang <1194593491@qq.com> Date: Mon, 8 Jul 2024 11:22:03 +0800 Subject: [PATCH 3/9] refactor(back-top): remove unnecessary type attributes --- src/back-top/type.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/back-top/type.ts b/src/back-top/type.ts index 141bffb..cf883f5 100644 --- a/src/back-top/type.ts +++ b/src/back-top/type.ts @@ -1,14 +1,6 @@ -import { AttachNode, Styles, TNode } from '../common'; +import { AttachNode, TNode } from '../common'; export interface TdBackTopProps { - /** - * 类名 - */ - className?: string; - /** - * 样式 - */ - style?: Styles; /** * 回到顶部的内容 */ From 7e54478130d2a504fcb4a6c8c68622e6bd24b45a Mon Sep 17 00:00:00 2001 From: Tang <1194593491@qq.com> Date: Mon, 8 Jul 2024 17:19:34 +0800 Subject: [PATCH 4/9] refactor(back-top): modify irregular code --- src/back-top/back-top.tsx | 123 +++++++++++++++++++++++------------- src/back-top/style/css.js | 1 - src/back-top/style/index.ts | 3 +- src/back-top/type.ts | 2 +- 4 files changed, 82 insertions(+), 47 deletions(-) delete mode 100644 src/back-top/style/css.js diff --git a/src/back-top/back-top.tsx b/src/back-top/back-top.tsx index 779cff5..f801d72 100644 --- a/src/back-top/back-top.tsx +++ b/src/back-top/back-top.tsx @@ -1,15 +1,13 @@ import 'tdesign-web-components/icon'; -import { Component, computed, createRef, effect, OmiProps, signal, SignalValue, tag } from 'omi'; +import { Component, createRef, OmiProps, signal, SignalValue, tag } from 'omi'; import classname, { getClassPrefix } from '../_util/classname'; import { scrollTo } from '../_util/dom.ts'; -import { StyledProps } from '../common'; +import { AttachNode, StyledProps } from '../common'; import { styleSheet } from './style'; import { TdBackTopProps } from './type.ts'; -type Element = HTMLElement | Window | Document; - export interface BackTopProps extends TdBackTopProps, StyledProps {} const getContainer = (container: string | Function) => { @@ -24,18 +22,44 @@ const getContainer = (container: string | Function) => { return null; }; +interface ClickEventProps { + target?: AttachNode; + container?: AttachNode; + duration?: number; + onClick?: OmiProps['onClick']; + e: MouseEvent; +} + @tag('t-back-top') export default class BackTop extends Component { static css = styleSheet; - buttonRef: Partial> = createRef(); + buttonRef = createRef(); - containerRef: Partial> = createRef(); + containerRef = createRef(); visible: SignalValue = signal(false); needUninstall: Set = new Set(); + cls: SignalValue = signal(''); + + static propTypes = { + children: Object, + container: Object, + content: Object, + default: Object, + duration: Number, + offset: Object, + shape: Object, + size: String, + target: Object, + theme: String, + visibleHeight: Object, + onClick: Function, + ignoreAttributes: Object, + }; + static defaultProps = { container: 'body', duration: 200, @@ -47,6 +71,22 @@ export default class BackTop extends Component { visibleHeight: '200px', }; + getBackTo(target: AttachNode, container: AttachNode): number { + if (target === container) return 0; + if (target === 'body') return 0; + if (!target) return 0; + const targetElement = getContainer(target); + if (!targetElement) return 0; + return (targetElement as HTMLElement).getBoundingClientRect().y; + } + + handleClick(clickEventProps: ClickEventProps) { + const { target, container, duration, onClick, e } = { ...clickEventProps }; + const y = this.getBackTo(target, container); + scrollTo(y, { container: this.containerRef.current, duration }); + onClick?.({ e }); + } + ready() { const { container, visibleHeight } = this.props; const scrollTop = signal(0); @@ -56,9 +96,10 @@ export default class BackTop extends Component { if (scrollContainer === document) { scrollTop.value = scrollContainer.documentElement.scrollTop; } else { - scrollTop.value = scrollContainer.scrollTop; + scrollTop.value = (scrollContainer as HTMLElement).scrollTop; } }; + const updateVisible = () => { if (typeof visibleHeight === 'string') { this.visible.value = scrollTop.value >= Number(visibleHeight.replace('px', '')); @@ -66,14 +107,18 @@ export default class BackTop extends Component { } this.visible.value = scrollTop.value >= visibleHeight; }; - scrollContainer.addEventListener('scroll', updateScrollTop); + + const updateEachScroll = () => { + updateScrollTop(); + updateVisible(); + }; + scrollContainer.addEventListener('scroll', updateEachScroll); + const scrollListenerUninstall = () => { - scrollContainer.removeEventListener('scroll', updateScrollTop); + scrollContainer.removeEventListener('scroll', updateEachScroll); }; - const visibleUninstall: Function = effect(updateVisible); this.needUninstall.add(scrollListenerUninstall); - this.needUninstall.add(visibleUninstall); } uninstall(): void { @@ -85,16 +130,16 @@ export default class BackTop extends Component { render(props: OmiProps) { const { theme, - size, - shape, target, + shape, + size, + className, container, duration, content, offset, children, default: cusContent, - className, style, onClick, ignoreAttributes, @@ -104,47 +149,39 @@ export default class BackTop extends Component { this.removeAttribute(attr); }); } - const backTopStyle: SignalValue = computed(() => ({ + const backTopStyle = { insetInlineEnd: offset[0], insetBlockEnd: offset[1], ...style, - })); + }; const classPrefix = getClassPrefix(); - const cls = computed(() => - classname( - `${classPrefix}-back-top`, - `${classPrefix}-back-top--theme-${theme}`, - `${classPrefix}-back-top--${shape}`, - { - [`${classPrefix}-back-top--show`]: this.visible.value, - [`${classPrefix}-size-s`]: size === 'small', - [`${classPrefix}-size-m`]: size === 'medium', - }, - className, - ), - ); const defaultContent = ( <> TOP ); + this.cls.value = classname( + `${classPrefix}-back-top`, + `${classPrefix}-back-top--theme-${theme}`, + `${classPrefix}-back-top--${shape}`, + { + [`${classPrefix}-back-top--show`]: this.visible.value, + [`${classPrefix}-size-s`]: size === 'small', + [`${classPrefix}-size-m`]: size === 'medium', + }, + className, + ); const renderChildren = children || content || cusContent || defaultContent; - const getBackTo = (): number => { - if (target === container) return 0; - if (target === 'body') return 0; - if (!target) return 0; - const targetElement = getContainer(target); - if (!targetElement) return 0; - return (targetElement as HTMLElement).getBoundingClientRect().y; - }; - const handleClick = (e: MouseEvent) => { - const y = getBackTo(); - scrollTo(y, { container: this.containerRef.current, duration }); - onClick?.({ e }); - }; + const clickEventProps = { target, container, duration, onClick } as ClickEventProps; return ( - ); diff --git a/src/back-top/style/css.js b/src/back-top/style/css.js deleted file mode 100644 index 6a9a4b1..0000000 --- a/src/back-top/style/css.js +++ /dev/null @@ -1 +0,0 @@ -import './index.css'; diff --git a/src/back-top/style/index.ts b/src/back-top/style/index.ts index 5c5f298..5f0722f 100644 --- a/src/back-top/style/index.ts +++ b/src/back-top/style/index.ts @@ -1,10 +1,9 @@ import { css, globalCSS } from 'omi'; import backTopStyle from '../../_common/style/web/components/back-top/_index.less'; -import theme from '../../_common/style/web/theme/_index.less'; export const styleSheet = css` - ${backTopStyle} + ${theme} + ${backTopStyle} `; globalCSS(styleSheet); diff --git a/src/back-top/type.ts b/src/back-top/type.ts index cf883f5..1ec3680 100644 --- a/src/back-top/type.ts +++ b/src/back-top/type.ts @@ -9,7 +9,7 @@ export interface TdBackTopProps { * 监听滚动的容器。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body * @default 'body' */ - container?: string | Function; + container?: AttachNode; /** * 回到顶部内容 */ From c86f6898391e719ef850f23caa7911ed8e2862bb Mon Sep 17 00:00:00 2001 From: Tang <1194593491@qq.com> Date: Tue, 9 Jul 2024 10:54:59 +0800 Subject: [PATCH 5/9] fix(back-top): solved the problem of event bubbling onClick --- src/back-top/back-top.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/back-top/back-top.tsx b/src/back-top/back-top.tsx index f801d72..87ed57e 100644 --- a/src/back-top/back-top.tsx +++ b/src/back-top/back-top.tsx @@ -82,6 +82,7 @@ export default class BackTop extends Component { handleClick(clickEventProps: ClickEventProps) { const { target, container, duration, onClick, e } = { ...clickEventProps }; + e.stopPropagation(); const y = this.getBackTo(target, container); scrollTo(y, { container: this.containerRef.current, duration }); onClick?.({ e }); From 9c81301c9f1ed7a42a0d7fbbb72fb33068eb8f0a Mon Sep 17 00:00:00 2001 From: Tang <1194593491@qq.com> Date: Wed, 10 Jul 2024 16:51:54 +0800 Subject: [PATCH 6/9] feat(back-top): update _common --- src/_common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_common b/src/_common index 4954259..7cea5c9 160000 --- a/src/_common +++ b/src/_common @@ -1 +1 @@ -Subproject commit 495425961c7b23dc8a38f8f0f841ea6487a9adc2 +Subproject commit 7cea5c951427ea7dc1e113cd4ff8053b4fa1a849 From eac01bd55811d8d072ddd717ec869e8273312420 Mon Sep 17 00:00:00 2001 From: Tang <1194593491@qq.com> Date: Wed, 10 Jul 2024 16:59:26 +0800 Subject: [PATCH 7/9] fix: remove unnecessary type attributes remove unnecessary type attributes re #74 --- src/back-top/back-top.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/back-top/back-top.tsx b/src/back-top/back-top.tsx index 87ed57e..782f2f4 100644 --- a/src/back-top/back-top.tsx +++ b/src/back-top/back-top.tsx @@ -45,7 +45,6 @@ export default class BackTop extends Component { cls: SignalValue = signal(''); static propTypes = { - children: Object, container: Object, content: Object, default: Object, From 6ae14841fd0de032791c17d3b5aa4ee7db8d6ab7 Mon Sep 17 00:00:00 2001 From: Tang <1194593491@qq.com> Date: Wed, 10 Jul 2024 18:38:14 +0800 Subject: [PATCH 8/9] fix(back-top): modify back-top propTypes --- src/back-top/back-top.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/back-top/back-top.tsx b/src/back-top/back-top.tsx index 782f2f4..270c15d 100644 --- a/src/back-top/back-top.tsx +++ b/src/back-top/back-top.tsx @@ -46,7 +46,7 @@ export default class BackTop extends Component { static propTypes = { container: Object, - content: Object, + content: [String, Number, Object, Function], default: Object, duration: Number, offset: Object, From 7f1c2691639d30df1273d059dd14c6a4f42a853d Mon Sep 17 00:00:00 2001 From: Tang <1194593491@qq.com> Date: Wed, 10 Jul 2024 18:53:09 +0800 Subject: [PATCH 9/9] fix(back-top): update icon use way --- src/back-top/back-top.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/back-top/back-top.tsx b/src/back-top/back-top.tsx index 270c15d..f905965 100644 --- a/src/back-top/back-top.tsx +++ b/src/back-top/back-top.tsx @@ -1,4 +1,4 @@ -import 'tdesign-web-components/icon'; +import 'tdesign-icons-web-components/esm/components/align-top'; import { Component, createRef, OmiProps, signal, SignalValue, tag } from 'omi'; @@ -157,7 +157,7 @@ export default class BackTop extends Component { const classPrefix = getClassPrefix(); const defaultContent = ( <> - + TOP );