Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/content/1.guide/22.build-your-own-hub-ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ Render the built-in variants of the open dock union
(`DevframeDockEntryRegistry`, `@devframes/hub/types`): `iframe` (the entry's
`url` in a kept-alive iframe, honoring `subTabs` soft nav), `action` (a
dock-rail button running its client script, shown as active while a
`toggle: true` action is on), `custom-render` (a container its
`toggle: true` action is on, which also closes `panel.session.open` without
clearing the selection, so show the panel only while it is `true`), `custom-render` (a container its
client script mounts into), `launcher` (a launch call-to-action reflecting
`launcher.status`), `group` (one dock-rail button collapsing its members), and
`~builtin` (your native views for reserved ids). What to render per type is in
Expand Down
12 changes: 9 additions & 3 deletions packages/hub-ui/src/client/components/dock/Dock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ const selectedEntry = computed(() => {
return context.docks.selected
})

// The bar marks the selection only while its panel shows: a toggle action
// hides the panel, and a click on the hidden view then reopens it.
const barSelectedEntry = computed(() => {
return context.panel.session.open ? selectedEntry.value : null
})

// Remember a recent dock whenever the selection lands on an entry that has
// no slot on the bar as currently rendered (grouped members always; top-level
// entries when picked from the overflow popover). Observing the selection
Expand Down Expand Up @@ -350,7 +356,7 @@ onMounted(() => {
:context="context"
:groups="splitEntries.visible"
:is-vertical="context.panel.isVertical"
:selected="selectedEntry"
:selected="barSelectedEntry"
@select="(e) => context.docks.switchEntry(e?.id)"
/>

Expand All @@ -360,7 +366,7 @@ onMounted(() => {
:context="context"
:entries="splitEntries.recent"
:is-vertical="context.panel.isVertical"
:selected="selectedEntry"
:selected="barSelectedEntry"
@select="(e) => context.docks.switchEntry(e?.id)"
/>
</template>
Expand All @@ -370,7 +376,7 @@ onMounted(() => {
:context="context"
:is-vertical="context.panel.isVertical"
:groups="splitEntries.overflow"
:selected="selectedEntry"
:selected="barSelectedEntry"
@select="(e) => context.docks.switchEntry(e?.id)"
@activity="bringUp"
/>
Expand Down
18 changes: 8 additions & 10 deletions packages/hub-ui/src/client/components/dock/DockEdge.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useEventListener } from '@vueuse/core'
import { computed, h, onMounted, ref, useTemplateRef, watch } from 'vue'
import { BUILTIN_ENTRY_SETTINGS } from '../../constants'
import { t } from '../../i18n'
import { docksSplitGroupsBySize, getEntryGroup } from '../../state/dock-settings'
import { docksSplitGroupsBySize, getEntryGroup, getPanelViewEntry } from '../../state/dock-settings'
import { setEdgePositionDropdown, setFloatingTooltip, useDocksGroupPanel, useDocksOverflowPanel, useEdgePositionDropdown } from '../../state/floating-tooltip'
import { useSettings } from '../../state/settings-defaults'
import { getEntryPaneKey, useIframePanes } from '../../utils/useIframePanes'
Expand Down Expand Up @@ -74,13 +74,11 @@ const splitEntries = computed(() => {
return docksSplitGroupsBySize(groupedEntries.value, (isVertical.value ? height : width) - settingsSize, item, gap, separatorSize)
})
const selectedEntry = computed(() => context.docks.selected)
// The bar marks the selection only while its panel shows: a toggle action
// hides the panel, and a click on the hidden view then reopens it.
const barSelectedEntry = computed(() => context.panel.session.open ? selectedEntry.value : null)
const activeGroup = computed(() => getEntryGroup(context.docks.entries, selectedEntry.value))
const hasPanelContent = computed(() => {
const entry = selectedEntry.value
return context.panel.session.open
&& !!entry
&& entry.type !== 'action'
})
const hasPanelContent = computed(() => !!getPanelViewEntry(context))

// Auto-collapse (on by default, opt out via `settings.autoCollapseEdgeToolbar:
// false`): idle-timeout + hover tracking, mirroring Dock.vue's own float-bar
Expand Down Expand Up @@ -480,7 +478,7 @@ const dragPreviewStyle = computed<CSSProperties | undefined>(() => {
:groups="splitEntries.visible"
:is-vertical="isVertical"
:rotate="false"
:selected="selectedEntry"
:selected="barSelectedEntry"
@select="(e) => context.docks.switchEntry(e?.id)"
/>
<DockOverflowButton
Expand All @@ -489,7 +487,7 @@ const dragPreviewStyle = computed<CSSProperties | undefined>(() => {
:groups="splitEntries.overflow"
:is-vertical="false"
:placement="positionDropdownPlacement[store.position]"
:selected="selectedEntry"
:selected="barSelectedEntry"
@select="(e) => context.docks.switchEntry(e?.id)"
@activity="bringUp"
/>
Expand All @@ -503,7 +501,7 @@ const dragPreviewStyle = computed<CSSProperties | undefined>(() => {
:context="context"
:entries="settingsEntries"
:is-vertical="false"
:selected="selectedEntry"
:selected="barSelectedEntry"
@select="(e) => context.docks.switchEntry(e?.id)"
/>
</div>
Expand Down
4 changes: 2 additions & 2 deletions packages/hub-ui/src/client/components/dock/DockPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { CSSProperties } from 'vue'
import type { DockLayout } from './dock-layout'
import { useElementBounding, useWindowSize } from '@vueuse/core'
import { computed, onMounted, reactive, ref, toRefs, useTemplateRef } from 'vue'
import { getEntryGroup } from '../../state/dock-settings'
import { getEntryGroup, getPanelViewEntry } from '../../state/dock-settings'
import { getEntryPaneKey, useIframePanes } from '../../utils/useIframePanes'
import ViewEntry from '../views/ViewEntry.vue'
import { DEFAULT_DOCK_LAYOUT, resolveDockAnchor } from './dock-layout'
Expand Down Expand Up @@ -173,7 +173,7 @@ onMounted(() => {

<template>
<div
v-show="context.docks.selected && context.docks.selected.type !== 'action'"
v-show="getPanelViewEntry(context)"
ref="dockPanel"
class="bg-dock-glass rounded-lg border border-base color-base shadow overflow-hidden"
:style="panelStyle"
Expand Down
22 changes: 22 additions & 0 deletions packages/hub-ui/src/client/state/client-script.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { createSharedState } from 'devframe/utils/shared-state'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { nextTick } from 'vue'
import { createDocksContext } from './context'
import { getPanelViewEntry } from './dock-settings'

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

it('hides the panel view while on and shows the same view and route again when off', async () => {
const { context } = await setup()
await context.docks.switchEntry('panel')
context.docks.selectedRoute = '/fixture/sub-tab'
expect(getPanelViewEntry(context)?.id).toBe('panel')

const offWays = [
() => context.docks.switchEntry('tool'),
() => context.docks.setActionActive('tool', false),
() => context.commands.execute('devframes:stop-active-tool'),
]
for (const turnOff of offWays) {
await context.docks.switchEntry('tool')
expect(getPanelViewEntry(context)).toBeNull()
await turnOff()
await nextTick()
expect(getPanelViewEntry(context)?.id).toBe('panel')
expect(context.docks.selectedRoute).toBe('/fixture/sub-tab')
}
})

it('turns off from the script or Escape and restores the panel', async () => {
const { context } = await setup()
await context.docks.switchEntry('panel')
Expand Down
13 changes: 13 additions & 0 deletions packages/hub-ui/src/client/state/dock-settings.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { DevframeDockEntriesGrouped, DevframeDockEntry, DevframeDocksUserSettings, DevframeViewGroup } from '@devframes/hub'
import type { DocksContext } from '@devframes/hub/client'
import type { Immutable } from 'devframe/utils/shared-state'
import type { WhenContext } from 'devframe/utils/when'
import type { MessageKey } from '../i18n'
Expand Down Expand Up @@ -134,6 +135,18 @@ export function getEntryGroup(
return group?.type === 'group' ? group : undefined
}

/**
* The entry the dock panel shows, or `null` when the panel is hidden. The panel
* shows only while `session.open` is true: a toggle action closes it and keeps
* the selection, so the same view comes back when the tool turns off.
*/
export function getPanelViewEntry(context: Pick<DocksContext, 'panel' | 'docks'>): DevframeDockEntry | null {
const entry = context.docks.selected
if (!context.panel.session.open || !entry || entry.type === 'action')
return null
return entry
}

/**
* Group a group's members by their **in-group sub-category** and sort them the
* same way the dock bar sorts (custom order, then default order). Members
Expand Down
Loading