Skip to content

Commit 0f9df73

Browse files
committed
feat: Optimize UI
1 parent 2bbbfef commit 0f9df73

File tree

17 files changed

+142
-45
lines changed

17 files changed

+142
-45
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"@radix-ui/react-switch": "^1.1.0",
5353
"@radix-ui/react-tabs": "^1.0.4",
5454
"@radix-ui/react-toggle": "^1.0.3",
55+
"@radix-ui/react-visually-hidden": "^1.1.0",
5556
"@reduxjs/toolkit": "^1.9.5",
5657
"@snyk/protect": "^1.1200.0",
5758
"@testing-library/jest-dom": "^5.17.0",

src/AppContainer.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Provider as ReduxProvider } from 'react-redux'
55
import Bootstrap from '@/bootstrap'
66
import { ThemeProvider } from '@/components/ThemeProvider'
77
import { UIProvider } from '@/components/UIProvider'
8+
import { SafeAreaInsetsProvider } from '@/hooks/useSafeAreaInsets'
89
import { store } from '@/store'
910

1011
const AppContainer: React.FC<{ children: ReactNode }> = ({ children }) => {
@@ -13,7 +14,9 @@ const AppContainer: React.FC<{ children: ReactNode }> = ({ children }) => {
1314
<HelmetProvider>
1415
<ThemeProvider>
1516
<UIProvider>
16-
<Bootstrap>{children}</Bootstrap>
17+
<SafeAreaInsetsProvider>
18+
<Bootstrap>{children}</Bootstrap>
19+
</SafeAreaInsetsProvider>
1720
</UIProvider>
1821
</ThemeProvider>
1922
</HelmetProvider>

src/components/ActionsModal.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { useTranslation } from 'react-i18next'
44
import { useResponsiveDialog } from '@/components/ResponsiveDialog'
55
import { Button } from '@/components/ui/button'
66
import type { Dialog } from '@/components/ui/dialog'
7-
import { BottomSafeArea } from '@/components/VerticalSafeArea'
87

98
export type Action = {
109
id: number | string
@@ -52,8 +51,6 @@ const ActionsModal = ({
5251
<Button variant="outline">{t('common.close')}</Button>
5352
</DialogClose>
5453
</DialogFooter>
55-
56-
<BottomSafeArea />
5754
</DialogContent>
5855
</Dialog>
5956
)

src/components/ResponsiveDialog.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import React, { memo, type ReactNode } from 'react'
2+
import { css } from '@emotion/react'
13
import tw from 'twin.macro'
24
import { useMediaQuery } from 'usehooks-ts'
35

@@ -23,8 +25,23 @@ import {
2325
} from '@/components/ui/drawer'
2426

2527
const CustomDrawerContent = tw(DrawerContent)`px-6`
26-
const CustomDrawerFooter = tw(DrawerFooter)`px-0`
2728
const CustomDrawerHeader = tw(DrawerHeader)`px-0`
29+
const CustomDrawerFooter = memo(function CustomDrawerFooter({
30+
children,
31+
}: {
32+
children: ReactNode
33+
}) {
34+
return (
35+
<DrawerFooter
36+
className="px-0"
37+
css={css`
38+
padding-bottom: max(env(safe-area-inset-bottom), 1rem);
39+
`}
40+
>
41+
{children}
42+
</DrawerFooter>
43+
)
44+
})
2845

2946
export const useResponsiveDialog = () => {
3047
const isDesktop = useMediaQuery('(min-width: 768px)')
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { createContext } from 'react'
2+
3+
export type Context = {
4+
top: number
5+
left: number
6+
right: number
7+
bottom: number
8+
}
9+
10+
const context = createContext<Context | null>(null)
11+
12+
export default context

src/hooks/useSafeAreaInsets/hooks.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from 'react'
2+
3+
import context from './context'
4+
5+
export const useSafeAreaInsets = () => {
6+
const safeAreaInsets = React.useContext(context)
7+
8+
if (safeAreaInsets === null) {
9+
throw new Error(
10+
'useSafeAreaInsets must be used within a SafeAreaInsetsProvider',
11+
)
12+
}
13+
14+
return safeAreaInsets
15+
}

src/hooks/useSafeAreaInsets/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './provider'
2+
export * from './hooks'
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React, { useEffect, useState } from 'react'
2+
3+
import context, { type Context } from './context'
4+
5+
type SafeAreaInsetsProviderProps = {
6+
children: React.ReactNode
7+
}
8+
9+
export const SafeAreaInsetsProvider = ({
10+
children,
11+
}: SafeAreaInsetsProviderProps) => {
12+
const [state, setState] = useState<Context | null>(null)
13+
14+
useEffect(() => {
15+
const tempDiv = document.createElement('div')
16+
17+
tempDiv.style.paddingTop = 'env(safe-area-inset-top, 0px)'
18+
tempDiv.style.paddingBottom = 'env(safe-area-inset-bottom, 0px)'
19+
tempDiv.style.paddingLeft = 'env(safe-area-inset-left, 0px)'
20+
tempDiv.style.paddingRight = 'env(safe-area-inset-right, 0px)'
21+
tempDiv.style.position = 'absolute'
22+
tempDiv.style.visibility = 'hidden'
23+
24+
document.body.appendChild(tempDiv)
25+
26+
const safeAreaInsetTop = window.getComputedStyle(tempDiv).paddingTop
27+
const safeAreaInsetBottom = window.getComputedStyle(tempDiv).paddingBottom
28+
const safeAreaInsetLeft = window.getComputedStyle(tempDiv).paddingLeft
29+
const safeAreaInsetRight = window.getComputedStyle(tempDiv).paddingRight
30+
31+
document.body.removeChild(tempDiv)
32+
33+
setState({
34+
top: parseInt(safeAreaInsetTop.replace('px', ''), 10),
35+
bottom: parseInt(safeAreaInsetBottom.replace('px', ''), 10),
36+
left: parseInt(safeAreaInsetLeft.replace('px', ''), 10),
37+
right: parseInt(safeAreaInsetRight.replace('px', ''), 10),
38+
})
39+
}, [])
40+
41+
return <context.Provider value={state}>{children}</context.Provider>
42+
}

src/pages/Home/components/SetHostModal.tsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,9 @@ import { Laptop } from 'lucide-react'
66
import store from 'store2'
77

88
import ProfileCell from '@/components/ProfileCell'
9+
import { useResponsiveDialog } from '@/components/ResponsiveDialog'
910
import { Badge } from '@/components/ui/badge'
1011
import { Button } from '@/components/ui/button'
11-
import {
12-
Dialog,
13-
DialogContent,
14-
DialogFooter,
15-
DialogHeader,
16-
DialogTitle,
17-
DialogTrigger,
18-
} from '@/components/ui/dialog'
1912
import { useAppDispatch, useProfile } from '@/store'
2013
import { profileActions } from '@/store/slices/profile'
2114
import { trafficActions } from '@/store/slices/traffic'
@@ -26,6 +19,16 @@ const SetHostModal: React.FC = () => {
2619
const { t } = useTranslation()
2720
const dispatch = useAppDispatch()
2821

22+
const {
23+
Dialog,
24+
DialogContent,
25+
DialogHeader,
26+
DialogTitle,
27+
DialogFooter,
28+
DialogTrigger,
29+
DialogClose,
30+
} = useResponsiveDialog()
31+
2932
const [existingProfiles, setExistingProfiles] = useState<Array<Profile>>([])
3033
const currentProfile = useProfile()
3134
const navigate = useNavigate()
@@ -69,7 +72,7 @@ const SetHostModal: React.FC = () => {
6972
<DialogTitle>{t('landing.history')}</DialogTitle>
7073
</DialogHeader>
7174

72-
<div className="bg-gray-100 dark:bg-muted border divide-y divide-gray-200 dark:divide-black/20 rounded-xl overflow-hidden">
75+
<div className="bg-gray-100 dark:bg-muted border divide-y divide-gray-200 dark:divide-black/20 rounded-xl overflow-hidden my-3">
7376
{existingProfiles.map((profile) => {
7477
return (
7578
<div
@@ -94,7 +97,10 @@ const SetHostModal: React.FC = () => {
9497
</div>
9598

9699
<DialogFooter>
97-
<Button className="mt-3 sm:mt-0" onClick={() => onAddNewProfile()}>
100+
<DialogClose asChild className="md:hidden">
101+
<Button variant="outline">{t('common.close')}</Button>
102+
</DialogClose>
103+
<Button onClick={() => onAddNewProfile()}>
98104
{t('landing.add_new_host')}
99105
</Button>
100106
</DialogFooter>

src/pages/Landing/Regular.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import DarkModeToggle from '@/components/DarkModeToggle'
1212
import ProfileCell from '@/components/ProfileCell'
1313
import RunInSurge from '@/components/RunInSurge'
1414
import { Button } from '@/components/ui/button'
15-
import { Checkbox } from '@/components/ui/checkbox'
1615
import {
1716
Form,
1817
FormControl,
@@ -23,6 +22,7 @@ import {
2322
FormMessage,
2423
} from '@/components/ui/form'
2524
import { Input } from '@/components/ui/input'
25+
import { Switch } from '@/components/ui/switch'
2626
import { TypographyH2, TypographyH4 } from '@/components/ui/typography'
2727
import VersionTag from '@/components/VersionTag'
2828
import InstallCertificateModal from '@/pages/Landing/components/InstallCertificateModal'
@@ -294,15 +294,15 @@ export const Component: React.FC = () => {
294294
)}
295295
/>
296296

297-
<div className="pt-2 space-y-2">
297+
<div className="pt-2 space-y-3">
298298
<RunInSurge not>
299299
<FormField
300300
control={form.control}
301301
name="useTls"
302302
render={({ field }) => (
303303
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
304304
<FormControl>
305-
<Checkbox
305+
<Switch
306306
disabled={protocol === 'https:'}
307307
checked={field.value}
308308
onCheckedChange={field.onChange}
@@ -320,7 +320,7 @@ export const Component: React.FC = () => {
320320
render={({ field }) => (
321321
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
322322
<FormControl>
323-
<Checkbox
323+
<Switch
324324
checked={field.value}
325325
onCheckedChange={field.onChange}
326326
/>

0 commit comments

Comments
 (0)