Skip to content

Commit

Permalink
feat: improve split screen (#358)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexzhang1030 authored May 3, 2024
1 parent a12c816 commit 0d95534
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 69 deletions.
49 changes: 49 additions & 0 deletions packages/client/src/components/CustomTabComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script setup lang="ts">
import { CustomTab } from '@vue/devtools-kit'
const p = defineProps<{
tab: CustomTab
iframeInline?: boolean
}>()
const props = toRefs(p)
const tabName = computed(() => props.tab.value.name)
const iframeViewVisible = ref(true)
watch(() => tabName.value, () => {
iframeViewVisible.value = false
setTimeout(() => {
iframeViewVisible.value = true
}, 100)
})
</script>

<template>
<template v-if="!tab">
<div flex="~ col" h-full items-center justify-center>
<div flex="~ col gap2" mxa items-center>
<div i-carbon-queued mb2 text-5xl op50 />
<p text-xl>
Tab <code text-rose>{{ tabName }}</code> not found
</p>
<p mt8 animate-pulse>
Redirecting to overview page...
</p>
</div>
</div>
</template>
<template v-else-if="tab?.view?.type === 'iframe'">
<IframeView v-if="iframeViewVisible" :src="tab.view.src" :inline="iframeInline" />
</template>
<template v-else-if="tab?.view?.type === 'vnode'">
<Component :is="tab.view.vnode" />
</template>
<template v-else>
<div>
<NCard flex="~ col" h-full items-center justify-center>
Unknown tab type {{ tab?.view }}
</NCard>
</div>
</template>
</template>
23 changes: 20 additions & 3 deletions packages/client/src/components/common/IframeView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ const iframeCacheMap = new Map<string, HTMLIFrameElement>()
</script>

<script setup lang="ts">
const props = defineProps<{
const props = withDefaults(defineProps<{
src: string
}>()
inline?: boolean
}>(), {
inline: false,
})
const { colorMode } = useDevToolsColorMode()
const anchor = ref<HTMLDivElement>()
Expand Down Expand Up @@ -38,10 +41,18 @@ onMounted(() => {
catch (e) {
iframeEl.value.style.opacity = '1'
}
document.body.appendChild(iframeEl.value)
nextTick(updateIframeBox)
}
// should force update the iframe visible on conflict(inline mode is unmounted after global mode is mounted)
const timer = setTimeout(resolveConflictVisible, 100)
setTimeout(syncColorMode, 100)
onUnmounted(() => {
clearTimeout(timer)
})
})
watchEffect(updateIframeBox)
Expand All @@ -52,6 +63,12 @@ onUnmounted(() => {
iframeEl.value.style.visibility = 'hidden'
})
function resolveConflictVisible() {
if (!iframeEl.value)
return
iframeEl.value.style.visibility = 'visible'
}
function syncColorMode() {
if (!iframeEl.value || !iframeEl.value.contentWindow)
return
Expand All @@ -72,7 +89,7 @@ function updateIframeBox() {
left: `${box.left}px`,
top: `${box.top}px`,
width: `${box.width}px`,
height: `${box.height}px`,
height: `${props.inline ? box.height - box.top : box.height}px`,
outline: 'none',
})
}
Expand Down
13 changes: 10 additions & 3 deletions packages/client/src/components/common/SideNavItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ const props = withDefaults(
tab: ModuleBuiltinTab
minimized?: boolean
target?: 'main' | 'side'
disabled?: boolean
}>(),
{
minimized: true,
target: 'main',
disabled: false,
},
)
const route = useRoute()
Expand All @@ -22,6 +24,8 @@ const badge = computed(() => 'badge' in props.tab && props.tab.badge?.())
const isActive = computed(() => route.path.startsWith(tabPath.value))
function onClick() {
if (props.disabled)
return
if ('onClick' in props.tab && props.tab.onClick)
props.tab.onClick()
else if (props.target === 'side')
Expand All @@ -35,11 +39,14 @@ function onClick() {
:is="target === 'main' ? RouterLink : 'button'"
:to="tabPath"
:flex="`~ items-center ${minimized ? 'justify-center' : 'justify-between'}`"
hover="bg-active op-100"

text-secondary relative block h-10 select-none op65
:disabled="disabled"
:class="[
disabled ? 'cursor-not-allowed op40!' : 'hover:(bg-active op-100)',
]"
:w="minimized ? '10' : 'full'"
:rounded="minimized ? 'xl' : ''"
:p="minimized ? '1' : 'x3'" text-secondary relative block h-10 select-none op65
:p="minimized ? '1' : 'x3'"
exact-active-class="!text-primary-600 bg-active op-100!"
@click="onClick"
>
Expand Down
66 changes: 41 additions & 25 deletions packages/client/src/components/common/SplitScreen.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<script setup lang="ts">
import { CustomTab } from '@vue/devtools-kit'
import { VueButton, VueCard, VueDropdown, VTooltip as vTooltip } from '@vue/devtools-ui'
import { ModuleBuiltinTab } from '~/types'
function close() {
devtoolsClientState.value.splitScreen.enabled = false
Expand All @@ -10,32 +12,45 @@ const { enabledTabs, flattenedTabs } = useAllTabs()
const router = useRouter()
const route = useRoute()
const PageComponent = shallowRef()
const customTabName = ref<string | null>(null)
function isMatchedWithRoute(tab?: typeof flattenedTabs['value'][number]) {
const routePath = route.path.startsWith('/') ? route.path.slice(1) : route.path
return tab && 'path' in tab && routePath === tab.path
const routeTabName = getRouteTabName()
if (!tab)
return false
return tab.name === routeTabName
}
const currentTab = computed(() => {
const tab = flattenedTabs.value.find(tab => tab.name === splitScreenState.value.view)
return isMatchedWithRoute(tab) ? undefined : tab
})
const mainViewName = computed(() =>
getRouteTabName(),
)
function getRouteTabName() {
return route.path.startsWith(`/${CUSTOM_TAB_VIEW}/`)
? route.path.slice(CUSTOM_TAB_VIEW.length + 2)
: route.path.startsWith('/')
? route.path.slice(1)
: route.path
}
watch(
() => currentTab.value,
(tab) => {
if (!tab)
return
const routes = router.getRoutes()
const matched = tab && 'path' in tab
? routes.find(route => route.path === (tab.path.startsWith('/') ? tab.path : `/${tab.path}`))
: routes.find(route => route.name === 'custom-tab') // TODO: custom tabs
// if it's the same route as the main view, skip
const path = route.path.startsWith('/') ? route.path.slice(1) : route.path
if (matched?.path === path || route.params?.name === tab.name) {
PageComponent.value = undefined
// check if is a custom tab
if ((tab as CustomTab).view) {
customTabName.value = tab.name
return
}
customTabName.value = null
const routes = router.getRoutes()
const matched = routes.find(route => route.path === `/${(tab as ModuleBuiltinTab).path}`)
const component = matched?.components?.default
if (typeof component === 'function')
PageComponent.value = defineAsyncComponent(component as any)
Expand All @@ -50,7 +65,7 @@ const showGridPanel = ref(false)

<template>
<div h-full h-screen of-hidden>
<div v-if="PageComponent && currentTab" border="b base" flex="~ gap1" z-99 px4 py3 navbar-glass>
<div v-if="(PageComponent || customTabName) && currentTab" border="b base" flex="~ gap1" z-99 px4 py3 navbar-glass>
<VueDropdown placement="bottom-start" :distance="12" :skidding="5" :shown="showGridPanel" trigger="click">
<div flex cursor-pointer items-center gap2>
<div i-carbon-chevron-down text-sm op50 />
Expand All @@ -66,7 +81,7 @@ const showGridPanel = ref(false)
</span>
</div>
<template #popper>
<TabsGrid :categories="enabledTabs" target="side" />
<TabsGrid :categories="enabledTabs" target="side" :disabled-items="[mainViewName]" />
</template>
</VueDropdown>
<div flex-auto />
Expand All @@ -78,21 +93,22 @@ const showGridPanel = ref(false)
<div i-carbon:side-panel-open />
</button>
</div>
<div v-if="PageComponent && currentTab" of-auto style="height: calc(100% - 50px)">
<!-- TODO: custom tabs -->
<!-- Tabs -->
<CustomTabComponent v-if="customTabName && currentTab" :tab="currentTab as CustomTab" class="h-[calc(100%-50px)]" iframe-inline of-auto />
<div v-else-if="PageComponent && currentTab" of-auto class="h-[calc(100%-50px)]">
<component :is="PageComponent" :key="`tab-${currentTab.name}`" />
</div>
<div v-else>
<span text-lg op50>
Select a tab to start
</span>
<VueCard px4 py2 bg-base>
<TabsGrid :categories="enabledTabs" target="side" />
</VueCard>
<VueButton type="warning" outlined mt2 @click="close">
Close Split Screen
</VueButton>
<div v-else class="h-full w-full $ui-fcc">
<div>
<span text-lg op50>
Select a tab to start
</span>
<VueCard px4 py2 bg-base>
<TabsGrid :categories="enabledTabs" target="side" :disabled-items="[mainViewName]" />
</VueCard>
<VueButton type="warning" outlined mt2 @click="close">
Close Split Screen
</VueButton>
</div>
</div>
</div>
</template>
2 changes: 2 additions & 0 deletions packages/client/src/components/common/TabsGrid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { CategorizedTabs } from '~/composables/state-tab'
defineProps<{
categories: CategorizedTabs
target: 'main' | 'side'
disabledItems?: string[]
}>()
</script>

Expand All @@ -16,6 +17,7 @@ defineProps<{
<SideNavItem
v-for="tab of tabs"
:key="tab.name"
:disabled="disabledItems?.includes(tab.name)"
:target="target"
:tab="tab"
/>
Expand Down
2 changes: 2 additions & 0 deletions packages/client/src/constants/tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,5 @@ export function getBuiltinTab(viteDetected: boolean, moduleDetectives?: Devtools
? tab
: tab.map(([_, tabs]) => [_, tabs.filter(t => !viteOnlyTabs.includes(t.name))])
}

export const CUSTOM_TAB_VIEW = 'custom-tab-view'
2 changes: 1 addition & 1 deletion packages/client/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const routes = [
{ path: '/assets', component: Assets },
{ path: '/graph', component: Graph },
{ path: '/settings', component: Settings },
{ path: '/custom-tab-view/:name', component: CustomTabView },
{ path: `/${CUSTOM_TAB_VIEW}/:name`, component: CustomTabView },
]

// @TODO: find a better way to handle it
Expand Down
35 changes: 1 addition & 34 deletions packages/client/src/pages/custom-tab-view.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,8 @@ onMounted(() => {
}, 2000)
}
})
const iframeViewVisible = ref(true)
watch(() => route.params.name, () => {
iframeViewVisible.value = false
setTimeout(() => {
iframeViewVisible.value = true
}, 100)
})
</script>

<template>
<template v-if="!tab">
<div flex="~ col" h-full items-center justify-center>
<div flex="~ col gap2" mxa items-center>
<div i-carbon-queued mb2 text-5xl op50 />
<p text-xl>
Tab <code text-rose>{{ tabName }}</code> not found
</p>
<p mt8 animate-pulse>
Redirecting to overview page...
</p>
</div>
</div>
</template>
<template v-else-if="tab?.view?.type === 'iframe'">
<IframeView v-if="iframeViewVisible" :src="tab.view.src" />
</template>
<template v-else-if="tab?.view?.type === 'vnode'">
<Component :is="tab.view.vnode" />
</template>
<template v-else>
<div>
<NCard flex="~ col" h-full items-center justify-center>
Unknown tab type {{ tab?.view }}
</NCard>
</div>
</template>
<CustomTabComponent :tab="tab" />
</template>
4 changes: 2 additions & 2 deletions packages/client/src/pages/graph.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ const navbarRef = ref<HTMLElement>()
</script>

<template>
<div flex="~ col" relative h-screen of-hidden panel-grids class="graph-body">
<div flex="~ col" relative h-full of-hidden panel-grids class="graph-body">
<GraphNavbar ref="navbarRef" />
<div ref="container" flex="1" />
<div ref="container" class="absolute h-full w-full" />
<GraphFileType />
<GraphDrawer :top="navbarRef" />
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/pages/overview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ onDevToolsClientConnected(() => {
</script>

<template>
<div h-screen w-full flex of-auto>
<div h-full w-full flex of-auto>
<div flex="~ col gap2" ma h-full max-w-300 w-full px20>
<div flex-auto />

Expand Down

0 comments on commit 0d95534

Please sign in to comment.