Skip to content

Commit 43cfb85

Browse files
antfubotclaude
andcommitted
fix(hub-ui): hide the panel while a toggle action is on
The floating dock panel showed whenever a dock was selected, so a toggle action set `session.open` to false but the panel stayed on the page. The panel now shows only while `session.open` is true, like edge mode. The selection stays, so the same view and route come back when the tool turns off. While the panel is hidden, the dock bar no longer marks the selected view as active. A click on that view now opens it again, instead of clearing the selection. Refs #469 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 7414432 commit 43cfb85

6 files changed

Lines changed: 56 additions & 16 deletions

File tree

‎docs/content/1.guide/22.build-your-own-hub-ui.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ Render the built-in variants of the open dock union
4040
(`DevframeDockEntryRegistry`, `@devframes/hub/types`): `iframe` (the entry's
4141
`url` in a kept-alive iframe, honoring `subTabs` soft nav), `action` (a
4242
dock-rail button running its client script, shown as active while a
43-
`toggle: true` action is on), `custom-render` (a container its
43+
`toggle: true` action is on, which also closes `panel.session.open` without
44+
clearing the selection, so show the panel only while it is `true`), `custom-render` (a container its
4445
client script mounts into), `launcher` (a launch call-to-action reflecting
4546
`launcher.status`), `group` (one dock-rail button collapsing its members), and
4647
`~builtin` (your native views for reserved ids). What to render per type is in

‎packages/hub-ui/src/client/components/dock/Dock.vue‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ const selectedEntry = computed(() => {
123123
return context.docks.selected
124124
})
125125
126+
// The bar marks the selection only while its panel shows: a toggle action
127+
// hides the panel, and a click on the hidden view then reopens it.
128+
const barSelectedEntry = computed(() => {
129+
return context.panel.session.open ? selectedEntry.value : null
130+
})
131+
126132
// Remember a recent dock whenever the selection lands on an entry that has
127133
// no slot on the bar as currently rendered (grouped members always; top-level
128134
// entries when picked from the overflow popover). Observing the selection
@@ -350,7 +356,7 @@ onMounted(() => {
350356
:context="context"
351357
:groups="splitEntries.visible"
352358
:is-vertical="context.panel.isVertical"
353-
:selected="selectedEntry"
359+
:selected="barSelectedEntry"
354360
@select="(e) => context.docks.switchEntry(e?.id)"
355361
/>
356362

@@ -360,7 +366,7 @@ onMounted(() => {
360366
:context="context"
361367
:entries="splitEntries.recent"
362368
:is-vertical="context.panel.isVertical"
363-
:selected="selectedEntry"
369+
:selected="barSelectedEntry"
364370
@select="(e) => context.docks.switchEntry(e?.id)"
365371
/>
366372
</template>
@@ -370,7 +376,7 @@ onMounted(() => {
370376
:context="context"
371377
:is-vertical="context.panel.isVertical"
372378
:groups="splitEntries.overflow"
373-
:selected="selectedEntry"
379+
:selected="barSelectedEntry"
374380
@select="(e) => context.docks.switchEntry(e?.id)"
375381
@activity="bringUp"
376382
/>

‎packages/hub-ui/src/client/components/dock/DockEdge.vue‎

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { useEventListener } from '@vueuse/core'
77
import { computed, h, onMounted, ref, useTemplateRef, watch } from 'vue'
88
import { BUILTIN_ENTRY_SETTINGS } from '../../constants'
99
import { t } from '../../i18n'
10-
import { docksSplitGroupsBySize, getEntryGroup } from '../../state/dock-settings'
10+
import { docksSplitGroupsBySize, getEntryGroup, getPanelViewEntry } from '../../state/dock-settings'
1111
import { setEdgePositionDropdown, setFloatingTooltip, useDocksGroupPanel, useDocksOverflowPanel, useEdgePositionDropdown } from '../../state/floating-tooltip'
1212
import { useSettings } from '../../state/settings-defaults'
1313
import { getEntryPaneKey, useIframePanes } from '../../utils/useIframePanes'
@@ -74,13 +74,11 @@ const splitEntries = computed(() => {
7474
return docksSplitGroupsBySize(groupedEntries.value, (isVertical.value ? height : width) - settingsSize, item, gap, separatorSize)
7575
})
7676
const selectedEntry = computed(() => context.docks.selected)
77+
// The bar marks the selection only while its panel shows: a toggle action
78+
// hides the panel, and a click on the hidden view then reopens it.
79+
const barSelectedEntry = computed(() => context.panel.session.open ? selectedEntry.value : null)
7780
const activeGroup = computed(() => getEntryGroup(context.docks.entries, selectedEntry.value))
78-
const hasPanelContent = computed(() => {
79-
const entry = selectedEntry.value
80-
return context.panel.session.open
81-
&& !!entry
82-
&& entry.type !== 'action'
83-
})
81+
const hasPanelContent = computed(() => !!getPanelViewEntry(context))
8482
8583
// Auto-collapse (on by default, opt out via `settings.autoCollapseEdgeToolbar:
8684
// false`): idle-timeout + hover tracking, mirroring Dock.vue's own float-bar
@@ -480,7 +478,7 @@ const dragPreviewStyle = computed<CSSProperties | undefined>(() => {
480478
:groups="splitEntries.visible"
481479
:is-vertical="isVertical"
482480
:rotate="false"
483-
:selected="selectedEntry"
481+
:selected="barSelectedEntry"
484482
@select="(e) => context.docks.switchEntry(e?.id)"
485483
/>
486484
<DockOverflowButton
@@ -489,7 +487,7 @@ const dragPreviewStyle = computed<CSSProperties | undefined>(() => {
489487
:groups="splitEntries.overflow"
490488
:is-vertical="false"
491489
:placement="positionDropdownPlacement[store.position]"
492-
:selected="selectedEntry"
490+
:selected="barSelectedEntry"
493491
@select="(e) => context.docks.switchEntry(e?.id)"
494492
@activity="bringUp"
495493
/>
@@ -503,7 +501,7 @@ const dragPreviewStyle = computed<CSSProperties | undefined>(() => {
503501
:context="context"
504502
:entries="settingsEntries"
505503
:is-vertical="false"
506-
:selected="selectedEntry"
504+
:selected="barSelectedEntry"
507505
@select="(e) => context.docks.switchEntry(e?.id)"
508506
/>
509507
</div>

‎packages/hub-ui/src/client/components/dock/DockPanel.vue‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { CSSProperties } from 'vue'
55
import type { DockLayout } from './dock-layout'
66
import { useElementBounding, useWindowSize } from '@vueuse/core'
77
import { computed, onMounted, reactive, ref, toRefs, useTemplateRef } from 'vue'
8-
import { getEntryGroup } from '../../state/dock-settings'
8+
import { getEntryGroup, getPanelViewEntry } from '../../state/dock-settings'
99
import { getEntryPaneKey, useIframePanes } from '../../utils/useIframePanes'
1010
import ViewEntry from '../views/ViewEntry.vue'
1111
import { DEFAULT_DOCK_LAYOUT, resolveDockAnchor } from './dock-layout'
@@ -173,7 +173,7 @@ onMounted(() => {
173173

174174
<template>
175175
<div
176-
v-show="context.docks.selected && context.docks.selected.type !== 'action'"
176+
v-show="getPanelViewEntry(context)"
177177
ref="dockPanel"
178178
class="bg-dock-glass rounded-lg border border-base color-base shadow overflow-hidden"
179179
:style="panelStyle"

‎packages/hub-ui/src/client/state/client-script.integration.test.ts‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { createSharedState } from 'devframe/utils/shared-state'
77
import { afterEach, describe, expect, it, vi } from 'vitest'
88
import { nextTick } from 'vue'
99
import { createDocksContext } from './context'
10+
import { getPanelViewEntry } from './dock-settings'
1011

1112
interface StubSharedState<Value extends object> extends SharedState<Value> {
1213
push: (value: Value) => void
@@ -354,6 +355,27 @@ describe('toggle action docks', () => {
354355
expect(events.at(-1)).toBe('on')
355356
})
356357

358+
it('hides the panel view while on and shows the same view and route again when off', async () => {
359+
const { context } = await setup()
360+
await context.docks.switchEntry('panel')
361+
context.docks.selectedRoute = '/fixture/sub-tab'
362+
expect(getPanelViewEntry(context)?.id).toBe('panel')
363+
364+
const offWays = [
365+
() => context.docks.switchEntry('tool'),
366+
() => context.docks.setActionActive('tool', false),
367+
() => context.commands.execute('devframes:stop-active-tool'),
368+
]
369+
for (const turnOff of offWays) {
370+
await context.docks.switchEntry('tool')
371+
expect(getPanelViewEntry(context)).toBeNull()
372+
await turnOff()
373+
await nextTick()
374+
expect(getPanelViewEntry(context)?.id).toBe('panel')
375+
expect(context.docks.selectedRoute).toBe('/fixture/sub-tab')
376+
}
377+
})
378+
357379
it('turns off from the script or Escape and restores the panel', async () => {
358380
const { context } = await setup()
359381
await context.docks.switchEntry('panel')

‎packages/hub-ui/src/client/state/dock-settings.ts‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { DevframeDockEntriesGrouped, DevframeDockEntry, DevframeDocksUserSettings, DevframeViewGroup } from '@devframes/hub'
2+
import type { DocksContext } from '@devframes/hub/client'
23
import type { Immutable } from 'devframe/utils/shared-state'
34
import type { WhenContext } from 'devframe/utils/when'
45
import type { MessageKey } from '../i18n'
@@ -134,6 +135,18 @@ export function getEntryGroup(
134135
return group?.type === 'group' ? group : undefined
135136
}
136137

138+
/**
139+
* The entry the dock panel shows, or `null` when the panel is hidden. The panel
140+
* shows only while `session.open` is true: a toggle action closes it and keeps
141+
* the selection, so the same view comes back when the tool turns off.
142+
*/
143+
export function getPanelViewEntry(context: Pick<DocksContext, 'panel' | 'docks'>): DevframeDockEntry | null {
144+
const entry = context.docks.selected
145+
if (!context.panel.session.open || !entry || entry.type === 'action')
146+
return null
147+
return entry
148+
}
149+
137150
/**
138151
* Group a group's members by their **in-group sub-category** and sort them the
139152
* same way the dock bar sorts (custom order, then default order). Members

0 commit comments

Comments
 (0)