Skip to content

Commit 8c71508

Browse files
committed
feat(ui): add back-top component
1 parent 1dbb55b commit 8c71508

37 files changed

Lines changed: 572 additions & 236 deletions

File tree

packages/site/src/app/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export function App() {
7171
const rootContext = useMemo<DConfigContextData>(
7272
() => ({
7373
i18n: { lang: i18n.language as DLang },
74-
updatePosition: { scroll: ['main.app-main'], resize: ['article.app-route-article'] },
74+
layout: { scrollEl: 'main.app-main', resizeEl: 'article.app-route-article' },
7575
}),
7676
[i18n.language]
7777
);

packages/site/src/app/components/layout/sidebar/Sidebar.tsx

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,29 +43,19 @@ export function AppSidebar(props: { aMenuOpen: boolean; onMenuOpenChange: (open:
4343
id: group.title,
4444
title: t(`menu-group.${group.title}`),
4545
type: 'group',
46-
children:
47-
group.title === 'Other'
48-
? [
49-
{
50-
id: 'Interface',
51-
title: (
52-
<Link to="/components/Interface">
53-
Interface{i18n.language !== 'en-US' && <span className="app-sidebar__subtitle">{t(`Documentation.Interface`)}</span>}
54-
</Link>
55-
),
56-
type: 'item',
57-
},
58-
]
59-
: group.children.map((child) => ({
60-
id: child.title,
61-
title: (
62-
<Link to={child.to}>
63-
{child.title}
64-
{i18n.language !== 'en-US' && <span className="app-sidebar__subtitle">{t(`menu.${child.title}`)}</span>}
65-
</Link>
66-
),
67-
type: 'item',
68-
})),
46+
children: (group.title === 'Other'
47+
? group.children.concat([{ title: 'Interface', to: '/components/Interface' }])
48+
: group.children
49+
).map((child) => ({
50+
id: child.title,
51+
title: (
52+
<Link to={child.to}>
53+
{child.title}
54+
{i18n.language !== 'en-US' && <span className="app-sidebar__subtitle">{t(`menu.${child.title}`)}</span>}
55+
</Link>
56+
),
57+
type: 'item',
58+
})),
6959
}))}
7060
dActive={activeId}
7161
onActiveChange={(id) => {

packages/ui/src/components/_date-input/DateInput.tsx

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,7 @@ import type { DFormControl } from '../form';
44
import React, { useEffect, useId, useImperativeHandle, useRef, useState } from 'react';
55
import ReactDOM from 'react-dom';
66

7-
import {
8-
usePrefixConfig,
9-
useTranslation,
10-
useAsync,
11-
useForkRef,
12-
useEventCallback,
13-
useMaxIndex,
14-
useElement,
15-
useUpdatePosition,
16-
} from '../../hooks';
7+
import { usePrefixConfig, useTranslation, useAsync, useForkRef, useEventCallback, useMaxIndex, useElement, useLayout } from '../../hooks';
178
import { CloseCircleFilled, SwapRightOutlined } from '../../icons';
189
import { checkNodeExist, getClassName, getNoTransformSize, getVerticalSidePosition } from '../../utils';
1910
import { TTANSITION_DURING_POPUP } from '../../utils/global';
@@ -75,6 +66,7 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
7566

7667
//#region Context
7768
const dPrefix = usePrefixConfig();
69+
const { dScrollEl, dResizeEl } = useLayout();
7870
//#endregion
7971

8072
//#region Ref
@@ -115,6 +107,8 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
115107

116108
const clearable = dClearable && !dVisible && !dDisabled;
117109

110+
const scrollEl = useElement(dScrollEl);
111+
const resizeEl = useElement(dResizeEl);
118112
const containerEl = useElement(() => {
119113
let el = document.getElementById(`${prefix}-root`);
120114
if (!el) {
@@ -145,8 +139,6 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
145139
}
146140
});
147141

148-
useUpdatePosition(updatePosition, dVisible);
149-
150142
useEffect(() => {
151143
if (dVisible) {
152144
const [asyncGroup, asyncId] = asyncCapture.createGroup();
@@ -169,6 +161,36 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
169161
}
170162
}, [asyncCapture, dVisible, uniqueId, updatePosition]);
171163

164+
useEffect(() => {
165+
if (dVisible && scrollEl) {
166+
const [asyncGroup, asyncId] = asyncCapture.createGroup();
167+
168+
asyncGroup.fromEvent(scrollEl, 'scroll', { passive: true }).subscribe({
169+
next: () => {
170+
updatePosition();
171+
},
172+
});
173+
174+
return () => {
175+
asyncCapture.deleteGroup(asyncId);
176+
};
177+
}
178+
}, [asyncCapture, dVisible, scrollEl, updatePosition]);
179+
180+
useEffect(() => {
181+
if (dVisible && resizeEl) {
182+
const [asyncGroup, asyncId] = asyncCapture.createGroup();
183+
184+
asyncGroup.onResize(resizeEl, () => {
185+
updatePosition();
186+
});
187+
188+
return () => {
189+
asyncCapture.deleteGroup(asyncId);
190+
};
191+
}
192+
}, [asyncCapture, dVisible, resizeEl, updatePosition]);
193+
172194
const preventBlur: React.MouseEventHandler = (e) => {
173195
if (e.target !== inputRefLeft.current && e.target !== inputRefRight.current && e.button === 0) {
174196
e.preventDefault();

packages/ui/src/components/_popup/Popup.tsx

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useEffect, useId, useRef } from 'react';
33
import ReactDOM from 'react-dom';
44
import { filter } from 'rxjs';
55

6-
import { useAsync, useElement, useEventCallback, usePrefixConfig, useUpdatePosition } from '../../hooks';
6+
import { useAsync, useElement, useEventCallback, useLayout, usePrefixConfig } from '../../hooks';
77

88
export interface DPopupPopupRenderProps {
99
'data-popup-popupid': string;
@@ -50,6 +50,7 @@ export function DPopup(props: DPopupProps): JSX.Element | null {
5050

5151
//#region Context
5252
const dPrefix = usePrefixConfig();
53+
const { dScrollEl, dResizeEl } = useLayout();
5354
//#endregion
5455

5556
const dataRef = useRef<{
@@ -60,19 +61,21 @@ export function DPopup(props: DPopupProps): JSX.Element | null {
6061

6162
const uniqueId = useId();
6263

63-
const containerEl = useElement(
64-
isUndefined(dContainer)
65-
? () => {
66-
let el = document.getElementById(`${dPrefix}popup-root`);
67-
if (!el) {
68-
el = document.createElement('div');
69-
el.id = `${dPrefix}popup-root`;
70-
document.body.appendChild(el);
71-
}
72-
return el;
73-
}
74-
: dContainer
75-
);
64+
const scrollEl = useElement(dScrollEl);
65+
const resizeEl = useElement(dResizeEl);
66+
const containerEl = useElement(() => {
67+
if (isUndefined(dContainer)) {
68+
let el = document.getElementById(`${dPrefix}popup-root`);
69+
if (!el) {
70+
el = document.createElement('div');
71+
el.id = `${dPrefix}popup-root`;
72+
document.body.appendChild(el);
73+
}
74+
return el;
75+
}
76+
77+
return dContainer;
78+
});
7679

7780
const changeVisible = (visible?: boolean) => {
7881
if (isUndefined(visible)) {
@@ -127,7 +130,7 @@ export function DPopup(props: DPopupProps): JSX.Element | null {
127130
asyncCapture.deleteGroup(asyncId);
128131
};
129132
}
130-
}, [asyncCapture, handleTrigger, dTrigger, dVisible, dDisabled]);
133+
}, [asyncCapture, dDisabled, dTrigger, dVisible, handleTrigger]);
131134

132135
useEffect(() => {
133136
if (!dDisabled && dVisible && dEscClosable) {
@@ -146,11 +149,7 @@ export function DPopup(props: DPopupProps): JSX.Element | null {
146149
asyncCapture.deleteGroup(asyncId);
147150
};
148151
}
149-
}, [asyncCapture, handleTrigger, dEscClosable, dVisible, dDisabled]);
150-
151-
useUpdatePosition(() => {
152-
dUpdatePosition?.();
153-
}, !dDisabled && dVisible);
152+
}, [asyncCapture, dDisabled, dEscClosable, dVisible, handleTrigger]);
154153

155154
useEffect(() => {
156155
if (!dDisabled && dVisible) {
@@ -174,8 +173,7 @@ export function DPopup(props: DPopupProps): JSX.Element | null {
174173
asyncCapture.deleteGroup(asyncId);
175174
};
176175
}
177-
// eslint-disable-next-line react-hooks/exhaustive-deps
178-
}, [asyncCapture, dVisible, uniqueId, dDisabled]);
176+
}, [asyncCapture, dDisabled, dUpdatePosition, dVisible, uniqueId]);
179177

180178
useEffect(() => {
181179
if (!dDisabled && dVisible && dContainer) {
@@ -195,8 +193,37 @@ export function DPopup(props: DPopupProps): JSX.Element | null {
195193
asyncCapture.deleteGroup(asyncId);
196194
};
197195
}
198-
// eslint-disable-next-line react-hooks/exhaustive-deps
199-
}, [asyncCapture, dContainer, dDisabled, dVisible]);
196+
}, [asyncCapture, dContainer, dDisabled, dUpdatePosition, dVisible]);
197+
198+
useEffect(() => {
199+
if (!dDisabled && dVisible && scrollEl) {
200+
const [asyncGroup, asyncId] = asyncCapture.createGroup();
201+
202+
asyncGroup.fromEvent(scrollEl, 'scroll', { passive: true }).subscribe({
203+
next: () => {
204+
dUpdatePosition?.();
205+
},
206+
});
207+
208+
return () => {
209+
asyncCapture.deleteGroup(asyncId);
210+
};
211+
}
212+
}, [asyncCapture, dDisabled, dUpdatePosition, dVisible, scrollEl]);
213+
214+
useEffect(() => {
215+
if (!dDisabled && dVisible && resizeEl) {
216+
const [asyncGroup, asyncId] = asyncCapture.createGroup();
217+
218+
asyncGroup.onResize(resizeEl, () => {
219+
dUpdatePosition?.();
220+
});
221+
222+
return () => {
223+
asyncCapture.deleteGroup(asyncId);
224+
};
225+
}
226+
}, [asyncCapture, dDisabled, dUpdatePosition, dVisible, resizeEl]);
200227

201228
const childProps: DPopupRenderProps = { 'data-popup-triggerid': uniqueId };
202229
if (!dDisabled) {

packages/ui/src/components/_selectbox/Selectbox.tsx

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,7 @@ import React, { useImperativeHandle, useRef, useState } from 'react';
55
import { useEffect } from 'react';
66
import ReactDOM from 'react-dom';
77

8-
import {
9-
useAsync,
10-
usePrefixConfig,
11-
useTranslation,
12-
useEventCallback,
13-
useElement,
14-
useMaxIndex,
15-
useUpdatePosition,
16-
useForkRef,
17-
} from '../../hooks';
8+
import { useAsync, usePrefixConfig, useTranslation, useEventCallback, useElement, useMaxIndex, useForkRef, useLayout } from '../../hooks';
189
import { CloseCircleFilled, DownOutlined, LoadingOutlined, SearchOutlined } from '../../icons';
1910
import { checkNodeExist, getClassName } from '../../utils';
2011
import { TTANSITION_DURING_POPUP } from '../../utils/global';
@@ -87,6 +78,7 @@ function Selectbox(props: DSelectboxProps, ref: React.ForwardedRef<DSelectboxRef
8778

8879
//#region Context
8980
const dPrefix = usePrefixConfig();
81+
const { dScrollEl, dResizeEl } = useLayout();
9082
//#endregion
9183

9284
//#region Ref
@@ -107,6 +99,8 @@ function Selectbox(props: DSelectboxProps, ref: React.ForwardedRef<DSelectboxRef
10799
const inputable = dSearchable && dVisible;
108100
const clearable = dClearable && !dVisible && !dLoading && !dDisabled && dContent;
109101

102+
const scrollEl = useElement(dScrollEl);
103+
const resizeEl = useElement(dResizeEl);
110104
const containerEl = useElement(() => {
111105
let el = document.getElementById(`${prefix}-root`);
112106
if (!el) {
@@ -134,8 +128,6 @@ function Selectbox(props: DSelectboxProps, ref: React.ForwardedRef<DSelectboxRef
134128
}
135129
});
136130

137-
useUpdatePosition(updatePosition, dVisible);
138-
139131
useEffect(() => {
140132
if (dVisible) {
141133
const [asyncGroup, asyncId] = asyncCapture.createGroup();
@@ -158,6 +150,36 @@ function Selectbox(props: DSelectboxProps, ref: React.ForwardedRef<DSelectboxRef
158150
}
159151
}, [asyncCapture, dVisible, updatePosition]);
160152

153+
useEffect(() => {
154+
if (dVisible && scrollEl) {
155+
const [asyncGroup, asyncId] = asyncCapture.createGroup();
156+
157+
asyncGroup.fromEvent(scrollEl, 'scroll', { passive: true }).subscribe({
158+
next: () => {
159+
updatePosition();
160+
},
161+
});
162+
163+
return () => {
164+
asyncCapture.deleteGroup(asyncId);
165+
};
166+
}
167+
}, [asyncCapture, dVisible, scrollEl, updatePosition]);
168+
169+
useEffect(() => {
170+
if (dVisible && resizeEl) {
171+
const [asyncGroup, asyncId] = asyncCapture.createGroup();
172+
173+
asyncGroup.onResize(resizeEl, () => {
174+
updatePosition();
175+
});
176+
177+
return () => {
178+
asyncCapture.deleteGroup(asyncId);
179+
};
180+
}
181+
}, [asyncCapture, dVisible, resizeEl, updatePosition]);
182+
161183
const preventBlur: React.MouseEventHandler = (e) => {
162184
if (e.target !== inputRef.current && e.button === 0) {
163185
e.preventDefault();

0 commit comments

Comments
 (0)