Skip to content

Commit

Permalink
feat: two-columns groups for mobile inspired by Clash Dash and Surge
Browse files Browse the repository at this point in the history
  • Loading branch information
Zephyruso committed Feb 24, 2025
1 parent 0d8db22 commit 9e9fc5f
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 15 deletions.
8 changes: 0 additions & 8 deletions src/assets/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,6 @@
}
}

.scrollbar-hidden::-webkit-scrollbar {
display: none;
}
.scrollbar-hidden {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}

.tippy-box {
@apply z-[9999] bg-neutral text-neutral-content shadow-md;
}
Expand Down
179 changes: 179 additions & 0 deletions src/components/proxies/ProxyGroupForMobile.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
<template>
<div
class="relative h-20"
ref="cardRef"
@click="handlerGroupClick"
>
<div
v-if="activeMode"
class="fixed inset-0 z-40 transition-all duration-200"
:class="modalMode && 'bg-black/50'"
></div>
<div
class="card overflow-hidden will-change-[height,width,transform]"
:class="[
activeMode ? `fixed z-50` : 'absolute left-0 top-0 h-auto w-full',
modalMode && 'left-1/2 top-1/2 max-h-[70vh] w-[96vw] -translate-x-1/2 -translate-y-1/2',
transitionAll && 'transition-all duration-200',
]"
:style="[
activeMode && {
'--tw-bg-opacity': 1,
},
activeMode &&
!modalMode && {
left: initX + 'px',
top: initY + 'px',
width: initWidth + 'px',
height: initHeight + 'px',
},
]"
@contextmenu.prevent.stop="handlerLatencyTest"
@transitionend="handlerTransitionEnd"
>
<div class="flex h-20 shrink-0 flex-col gap-1 p-2">
<ProxyIcon
v-if="proxyGroup.icon"
:icon="proxyGroup.icon"
size="small"
class="absolute right-2 top-2 z-[-1] h-10 !w-10"
/>
<div class="text-md truncate">
{{ proxyGroup.name }}
</div>
<div class="h-4 truncate text-xs text-base-content/80">
{{ proxyGroup.now }}
</div>

<div class="flex h-4 justify-between gap-1">
<span class="text-xs text-base-content/60">
{{ proxyGroup.type }} ({{ proxiesCount }})
</span>
<button
v-if="manageHiddenGroup"
class="btn btn-circle btn-xs z-10 ml-1"
@click.stop="handlerGroupToggle"
>
<EyeIcon
v-if="!hiddenGroupMap[proxyGroup.name]"
class="h-3 w-3"
/>
<EyeSlashIcon
v-else
class="h-3 w-3"
/>
</button>
<LatencyTag
:class="twMerge('z-10 bg-base-200/40 hover:shadow')"
:loading="isLatencyTesting"
:name="proxyGroup.now"
:group-name="proxyGroup.name"
@click.stop="handlerLatencyTest"
/>
</div>
</div>

<div
v-if="activeMode"
class="grid flex-1 grid-cols-2 gap-2 overflow-y-auto overflow-x-hidden p-2"
>
<ProxyNodeCard
v-for="node in renderProxies"
:key="node"
:name="node"
:group-name="proxyGroup.name"
:active="node === proxyGroup.now"
@click.stop="handlerProxySelect(node)"
/>
</div>
</div>
</div>
</template>

<script setup lang="ts">
import { useRenderProxies } from '@/composables/renderProxies'
import { PROXY_TYPE } from '@/constant'
import { hiddenGroupMap, proxyGroupLatencyTest, proxyMap, selectProxy } from '@/store/proxies'
import { manageHiddenGroup } from '@/store/settings'
import { EyeIcon, EyeSlashIcon } from '@heroicons/vue/24/outline'
import { twMerge } from 'tailwind-merge'
import { computed, nextTick, ref } from 'vue'
import LatencyTag from './LatencyTag.vue'
import ProxyIcon from './ProxyIcon.vue'
import ProxyNodeCard from './ProxyNodeCard.vue'
const props = defineProps<{
name: string
}>()
const proxyGroup = computed(() => proxyMap.value[props.name])
const allProxies = computed(() => proxyGroup.value.all ?? [])
const { proxiesCount, renderProxies } = useRenderProxies(allProxies, props.name)
const isLatencyTesting = ref(false)
const activeMode = ref(false)
const modalMode = ref(activeMode.value)
const cardRef = ref()
const initX = ref(0)
const initY = ref(0)
const initWidth = ref(0)
const initHeight = ref(0)
const transitionAll = ref(false)
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
const transitionEndCallback = ref<() => void>(() => {})
const handlerTransitionEnd = () => {
transitionEndCallback.value()
}
const handlerGroupClick = async () => {
if (!activeMode.value) {
const { x, y, width, height } = cardRef.value.getBoundingClientRect()
initX.value = x
initY.value = y
initWidth.value = width
initHeight.value = height
}
transitionEndCallback.value = () => {}
if (activeMode.value) {
transitionAll.value = true
modalMode.value = false
transitionEndCallback.value = async () => {
transitionAll.value = false
await nextTick()
activeMode.value = false
}
} else {
transitionAll.value = false
activeMode.value = true
await sleep(50)
transitionAll.value = true
modalMode.value = true
}
}
const handlerLatencyTest = async () => {
if (isLatencyTesting.value) return
isLatencyTesting.value = true
try {
await proxyGroupLatencyTest(props.name)
isLatencyTesting.value = false
} catch {
isLatencyTesting.value = false
}
}
const handlerGroupToggle = () => {
hiddenGroupMap.value[props.name] = !hiddenGroupMap.value[props.name]
}
const handlerProxySelect = (name: string) => {
if (proxyGroup.value.type.toLowerCase() === PROXY_TYPE.LoadBalance) return
selectProxy(props.name, name)
}
</script>
2 changes: 1 addition & 1 deletion src/components/settings/ProxiesSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
</div>
<div class="divider"></div>
<div class="grid grid-cols-1 gap-2 lg:grid-cols-2">
<div class="flex items-center gap-2 max-sm:hidden">
<div class="flex items-center gap-2">
{{ $t('twoColumnProxyGroup') }}
<input
class="toggle"
Expand Down
1 change: 1 addition & 0 deletions src/helper/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useMediaQuery } from '@vueuse/core'

export const twoColumn = useMediaQuery('(min-width: 810px)')
export const twoColumnWithSidebar = useMediaQuery('(min-width: 1130px)')
export const isSmallScreen = useMediaQuery('(max-width: 640px)')
export const isMiddleScreen = useMediaQuery('(max-width: 768px)')
export const isPWA = (() => {
return window.matchMedia('(display-mode: standalone)').matches || navigator.standalone
Expand Down
24 changes: 18 additions & 6 deletions src/views/ProxiesPage.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<template>
<div class="h-full overflow-y-scroll p-2 sm:pr-1">
<div
class="h-full overflow-y-scroll p-2 sm:pr-1"
:class="displayTwoColumns && 'max-sm:scrollbar-hidden'"
>
<template v-if="displayTwoColumns">
<div class="grid grid-cols-2 gap-1">
<div
Expand Down Expand Up @@ -32,23 +35,32 @@

<script setup lang="ts">
import ProxyGroup from '@/components/proxies/ProxyGroup.vue'
import ProxyGroupForMobile from '@/components/proxies/ProxyGroupForMobile.vue'
import ProxyProvider from '@/components/proxies/ProxyProvider.vue'
import { useProxies } from '@/composables/proxies'
import { PROXY_TAB_TYPE } from '@/constant'
import { twoColumn, twoColumnWithSidebar } from '@/helper/utils'
import { isSmallScreen, twoColumn, twoColumnWithSidebar } from '@/helper/utils'
import { fetchProxies } from '@/store/proxies'
import { isSidebarCollapsed, twoColumnProxyGroup } from '@/store/settings'
import { computed } from 'vue'
const { proxiesTabShow, renderGroups } = useProxies()
const Comp = computed(() =>
proxiesTabShow.value === PROXY_TAB_TYPE.PROVIDER ? ProxyProvider : ProxyGroup,
)
const isLargeScreen = computed(() => {
return isSidebarCollapsed.value ? twoColumn.value : twoColumnWithSidebar.value
})
const Comp = computed(() => {
if (proxiesTabShow.value === PROXY_TAB_TYPE.PROVIDER) {
return ProxyProvider
}
return isSmallScreen.value && displayTwoColumns.value ? ProxyGroupForMobile : ProxyGroup
})
const displayTwoColumns = computed(() => {
return (
(isSidebarCollapsed.value ? twoColumn.value : twoColumnWithSidebar.value) &&
(isLargeScreen.value || isSmallScreen.value) &&
twoColumnProxyGroup.value &&
renderGroups.value.length > 1
)
Expand Down
3 changes: 3 additions & 0 deletions tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export default {
'.scrollbar-thin': {
'scrollbar-width': 'thin',
},
'.scrollbar-hidden': {
'scrollbar-width': 'none',
},
})
}),
daisyui,
Expand Down

0 comments on commit 9e9fc5f

Please sign in to comment.