Skip to content

feat(DocView): bump dockview v4.4.0#481

Merged
ArgoZhang merged 5 commits intomasterfrom
feat-dockview
Jul 6, 2025
Merged

feat(DocView): bump dockview v4.4.0#481
ArgoZhang merged 5 commits intomasterfrom
feat-dockview

Conversation

@zhaijunlei955
Copy link
Copy Markdown
Contributor

@zhaijunlei955 zhaijunlei955 commented Jul 3, 2025

Link issues

fixes #482

Summary By Copilot

Regression?

  • Yes
  • No

Risk

  • High
  • Medium
  • Low

Verification

  • Manual (required)
  • Automated

Packaging changes reviewed?

  • Yes
  • No
  • N/A

☑️ Self Check before Merge

⚠️ Please check all items below before review. ⚠️

  • Doc is updated/provided or not needed
  • Demo is updated/provided or not needed
  • Merge the latest code from the main branch

Summary by Sourcery

Improve pop-out window handling by adding failure events and automatic repositioning, enhance floating group persistence and dropdown rendering, bump version, and clean up observers and configuration reload ordering.

New Features:

  • Add onDidOpenPopoutWindowFail event to handle blocked or failed pop-out window creation
  • Revert groups and restore visibility when pop-out window creation fails

Bug Fixes:

  • Ensure layoutConfig reload runs after lock and theme updates in initDockview
  • Disconnect group MutationObserver instances on component dispose to prevent memory leaks

Enhancements:

  • Persist floating group panel dimensions in layout config with sensible defaults
  • Dynamically reposition dropdown content for floating groups with always-rendered panels via MutationObserver
  • Bump dockview-core version to 4.4.0

Chores:

  • Add CSS for watermark container and border styling on the resize container in bb-dockview theme

@bb-auto
Copy link
Copy Markdown

bb-auto Bot commented Jul 3, 2025

Thanks for your PR, @zhaijunlei955. Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Jul 3, 2025

Reviewer's Guide

This PR upgrades dockview-core to v4.4.0, introduces a new popout failure event, enhances popout handling and configuration persistence, adds a dropdown display observer for floating panels, reorders layout reload logic, and includes related CSS adjustments.

Sequence diagram for popout window creation and failure handling

sequenceDiagram
    participant User as actor User
    participant DockviewComponent
    participant PopoutWindow
    participant Group
    User->>DockviewComponent: Request to pop out a group
    DockviewComponent->>PopoutWindow: Attempt to create popout window
    alt Popout window creation fails
        DockviewComponent->>DockviewComponent: Fire onDidOpenPopoutWindowFail
        DockviewComponent->>Group: Move group back to reference group
        DockviewComponent->>Group: Set group visible
    else Popout window creation succeeds
        DockviewComponent->>Group: Create floating group
    end
Loading

Sequence diagram for group dropdown display observer in floating panels

sequenceDiagram
    participant Group
    participant Icon
    participant MutationObserver
    Group->>Icon: Add dropdown icon
    Icon->>MutationObserver: Observe class changes on dropdown menu
    MutationObserver-->>Group: On 'show' class, move panel content to dv-content-container
    MutationObserver-->>Group: On class removal, move content back to wrapper
    Group->>MutationObserver: Disconnect on group dispose
Loading

Class diagram for DockviewComponent and new popout failure event

classDiagram
    class DockviewComponent {
        +onDidOpenPopoutWindowFail
        +_onDidOpenPopoutWindowFail
    }
    class DockviewApi {
        +onDidOpenPopoutWindowFail
    }
    DockviewApi --> DockviewComponent : uses
    DockviewComponent : +_onDidOpenPopoutWindowFail = new Emitter()
    DockviewComponent : +onDidOpenPopoutWindowFail = this._onDidOpenPopoutWindowFail.event
    DockviewApi : +get onDidOpenPopoutWindowFail()
Loading

Class diagram for group dropdown display observer logic

classDiagram
    class Group {
        +mutationObserver
        +panels
        +element
        +activePanel
    }
    class observeDisplayChange {
        +MutationObserver
    }
    Group --> observeDisplayChange : uses
    observeDisplayChange : +observe(icon, group)
    Group : +mutationObserver
Loading

Flow diagram for saveConfig floating group position persistence

flowchart TD
    A[saveConfig called] --> B{floatingGroups exist?}
    B -- No --> C[Save config to localStorage]
    B -- Yes --> D[For each floatingGroup]
    D --> E{fg.position.width > 0?}
    E -- Yes --> F[Set panel.params.currentPosition.width]
    E -- No --> G[Set fg.position.width from group.params.currentPosition.width or 500]
    D --> H{fg.position.height > 0?}
    H -- Yes --> I[Set panel.params.currentPosition.height]
    H -- No --> J[Set fg.position.height from group.params.currentPosition.height or 350]
    F & G & I & J --> C
Loading

File-Level Changes

Change Details Files
Bumped dockview-core version and corrected typo
  • Updated version from 4.3.1 to 4.4.0
  • Fixed comment typo from “occurance” to “occurrence”
src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-core.esm.js
Introduced onDidOpenPopoutWindowFail event
  • Exposed new API getter
  • Created Emitter and public event in the component
  • Registered the event in disposables
src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-core.esm.js
Improved popout window failure logic
  • Logged error for blocked pop-ups
  • Fired failure event and reverted group movement
  • Restored reference group visibility on failure
src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-core.esm.js
Implemented dropdown display observer for floating panels
  • Added MutationObserver in observeDisplayChange
  • Moved panel wrapper on dropdown show/hide
  • Disconnected observer on group and component dispose
src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js
src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-extensions.js
Persisted floating panel sizes in config
  • Saved panel.params.currentPosition width/height
  • Fell back to stored dimensions or default values
src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js
Reordered layoutConfig reload logic
  • Moved reloadFromConfig call after lock and theme updates
src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-utils.js
Updated CSS for watermark and resize container
  • Set .dv-watermark to flex and full height
  • Added border style to .dv-resize-container in BB theme
src/components/BootstrapBlazor.DockView/wwwroot/css/dockview.css
src/components/BootstrapBlazor.DockView/wwwroot/css/dockview-bb.css

Possibly linked issues

  • #0: PR updates DockView version 4.3.1 to 4.4.0, addressing issue request.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@bb-auto bb-auto Bot requested a review from ArgoZhang July 3, 2025 04:07
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @zhaijunlei955 - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js:190` </location>
<code_context>
 }
+const observeDisplayChange = (icon, group) => {
+    const dockview = group.api.accessor
+    const element = icon.querySelector('.dropdown-menu')
+    const mutationObserver = new MutationObserver((mutations) => {
+        mutations.forEach(mutation => {
</code_context>

<issue_to_address>
Potential null reference if '.dropdown-menu' is not found.

Add a null check for 'element' before creating the MutationObserver to prevent runtime errors.
</issue_to_address>

### Comment 2
<location> `src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js:202` </location>
<code_context>
+                    childEle.wrapperEle = currentPanelEle
+                }
+                else {
+                    const panelEleList = [...group.element.querySelector('&>.dv-content-container').children].map(item => {
+                        const wrapperEle = item.wrapperEle
+                        delete item.wrapperEle
</code_context>

<issue_to_address>
Repeated use of non-standard selector '&>.dv-content-container'.

This selector may not function as expected, which could cause 'panelEleList' to be empty or undefined and lead to errors.
</issue_to_address>

### Comment 3
<location> `src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js:196` </location>
<code_context>
+            if (mutation.attributeName == 'class') {
+                if(mutation.target.classList.contains('show')) {
+                    const currentPanelEle = group.activePanel.view.content.element.parentElement
+                    const childEle = currentPanelEle.children[0]
+                    group.element.querySelector('&>.dv-content-container').append(childEle)
+                    currentPanelEle.style.zIndex = -1
</code_context>

<issue_to_address>
No check for existence of 'currentPanelEle' or its children.

Accessing properties of a null 'currentPanelEle' or missing children will cause a runtime error. Please add null and existence checks before using these elements.
</issue_to_address>

### Comment 4
<location> `src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js:199` </location>
<code_context>
+                    const childEle = currentPanelEle.children[0]
+                    group.element.querySelector('&>.dv-content-container').append(childEle)
+                    currentPanelEle.style.zIndex = -1
+                    childEle.wrapperEle = currentPanelEle
+                }
+                else {
</code_context>

<issue_to_address>
Attaching custom properties to DOM elements can cause memory leaks.

Instead of assigning 'wrapperEle' directly to the DOM node, use a WeakMap or similar structure to store metadata and avoid potential memory leaks.
</issue_to_address>

### Comment 5
<location> `src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js:213` </location>
<code_context>
+            }
+        })
+    });
+    group.mutationObserver = mutationObserver
+    mutationObserver.observe(element, {
+        attributes: true,
</code_context>

<issue_to_address>
Potential for multiple observers if 'observeDisplayChange' is called multiple times.

Disconnect any existing observer before assigning a new one to prevent resource leaks.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
    group.mutationObserver = mutationObserver
    mutationObserver.observe(element, {
        attributes: true,
        attributeFilter: ["class"],
    });
}
=======
    if (group.mutationObserver) {
        group.mutationObserver.disconnect();
    }
    group.mutationObserver = mutationObserver
    mutationObserver.observe(element, {
        attributes: true,
        attributeFilter: ["class"],
    });
}
>>>>>>> REPLACE

</suggested_fix>

### Comment 6
<location> `src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-config.js:321` </location>
<code_context>
-                fg.position.width = fg.position.width || (width ? width + 2 : 300)
-                fg.position.height = fg.position.height || dockview.floatingGroups[index].group.height
+                const group = dockview.floatingGroups[index].group
+                if (fg.position.width > 0) {
+                    group.panels.forEach(panel => {
+                        !panel.params.currentPosition && (panel.params.currentPosition = {})
+                        panel.params.currentPosition.width = fg.position.width
+                    })
+                }
+                else {
+                    fg.position.width = group.params.currentPosition.width || 500
+                }
+                if (fg.position.height > 0) {
</code_context>

<issue_to_address>
Possible undefined 'group.params.currentPosition' access.

Accessing 'width' or 'height' on an undefined 'currentPosition' will cause a runtime error. Please add a check to ensure 'currentPosition' is defined before accessing its properties.
</issue_to_address>

### Comment 7
<location> `src/components/BootstrapBlazor.DockView/wwwroot/js/dockview-group.js:188` </location>
<code_context>
     }, 0)
     addActionEvent(group, actionContainer);
 }
+const observeDisplayChange = (icon, group) => {
+    const dockview = group.api.accessor
+    const element = icon.querySelector('.dropdown-menu')
</code_context>

<issue_to_address>
Consider replacing the MutationObserver logic with Bootstrap dropdown event listeners and extracting the panel move logic into two helper functions.

```markdown
You can simplify/remove the MutationObserver boilerplate by listening to the dropdown’s built-in events (e.g. Bootstrap’s `show.bs.dropdown`/`hide.bs.dropdown`) and extracting the “portal” logic into two small helpers. This keeps the same functionality but is far more declarative and easier to follow.

```js
// 1) In createGroupActions, replace observeDisplayChange(…) with two event listeners:
if (icon.classList.contains('bb-dockview-control-icon-dropdown')) {
  // bootstrap dropdown events
  icon.addEventListener('show.bs.dropdown',  () => portalPanelIntoDropdown(group));
  icon.addEventListener('hide.bs.dropdown',  () => restorePanelFromDropdown(group));
}
```

```js
// 2) Move all the move/unwrap logic into two focused functions:

function portalPanelIntoDropdown(group) {
  const panelEle       = group.activePanel.view.content.element;
  const wrapper        = panelEle.parentElement;
  const container      = group.element.querySelector(':scope > .dv-content-container');

  // “portal” the panel into dropdown
  wrapper.style.zIndex = '-1';
  panelEle.__wrapper   = wrapper;            // temp link back to wrapper
  container.appendChild(panelEle);
}

function restorePanelFromDropdown(group) {
  const container = group.element.querySelector(':scope > .dv-content-container');
  Array.from(container.children).forEach(child => {
    const wrapper = child.__wrapper;
    delete child.__wrapper;

    // move child back into its original wrapper, reset z-index
    wrapper.style.zIndex = '';
    wrapper.appendChild(child);
  });

  // re-attach wrappers back into the dockview DOM
  const panels = Array.from(group.element.querySelector(':scope > .dv-content-container').children)
    .map(c => c.__wrapper);
  group.element.parentElement.parentElement.append(...panels);
}
```

Benefits:
- No manual `MutationObserver` or `setTimeout`
- Clear semantic hooks (`show.bs.dropdown` / `hide.bs.dropdown`)
- Portal logic lives in two tiny, testable functions
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

}
const observeDisplayChange = (icon, group) => {
const dockview = group.api.accessor
const element = icon.querySelector('.dropdown-menu')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Potential null reference if '.dropdown-menu' is not found.

Add a null check for 'element' before creating the MutationObserver to prevent runtime errors.

childEle.wrapperEle = currentPanelEle
}
else {
const panelEleList = [...group.element.querySelector('&>.dv-content-container').children].map(item => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Repeated use of non-standard selector '&>.dv-content-container'.

This selector may not function as expected, which could cause 'panelEleList' to be empty or undefined and lead to errors.

if (mutation.attributeName == 'class') {
if(mutation.target.classList.contains('show')) {
const currentPanelEle = group.activePanel.view.content.element.parentElement
const childEle = currentPanelEle.children[0]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): No check for existence of 'currentPanelEle' or its children.

Accessing properties of a null 'currentPanelEle' or missing children will cause a runtime error. Please add null and existence checks before using these elements.

const childEle = currentPanelEle.children[0]
group.element.querySelector('&>.dv-content-container').append(childEle)
currentPanelEle.style.zIndex = -1
childEle.wrapperEle = currentPanelEle
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Attaching custom properties to DOM elements can cause memory leaks.

Instead of assigning 'wrapperEle' directly to the DOM node, use a WeakMap or similar structure to store metadata and avoid potential memory leaks.

Comment on lines +213 to +218
group.mutationObserver = mutationObserver
mutationObserver.observe(element, {
attributes: true,
attributeFilter: ["class"],
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Potential for multiple observers if 'observeDisplayChange' is called multiple times.

Disconnect any existing observer before assigning a new one to prevent resource leaks.

Suggested change
group.mutationObserver = mutationObserver
mutationObserver.observe(element, {
attributes: true,
attributeFilter: ["class"],
});
}
if (group.mutationObserver) {
group.mutationObserver.disconnect();
}
group.mutationObserver = mutationObserver
mutationObserver.observe(element, {
attributes: true,
attributeFilter: ["class"],
});
}

Comment on lines +321 to +328
if (fg.position.width > 0) {
group.panels.forEach(panel => {
!panel.params.currentPosition && (panel.params.currentPosition = {})
panel.params.currentPosition.width = fg.position.width
})
}
else {
fg.position.width = group.params.currentPosition.width || 500
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Possible undefined 'group.params.currentPosition' access.

Accessing 'width' or 'height' on an undefined 'currentPosition' will cause a runtime error. Please add a check to ensure 'currentPosition' is defined before accessing its properties.

}, 0)
addActionEvent(group, actionContainer);
}
const observeDisplayChange = (icon, group) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider replacing the MutationObserver logic with Bootstrap dropdown event listeners and extracting the panel move logic into two helper functions.

You can simplify/remove the MutationObserver boilerplate by listening to the dropdown’s built-in events (e.g. Bootstrap’s `show.bs.dropdown`/`hide.bs.dropdown`) and extracting the “portal” logic into two small helpers. This keeps the same functionality but is far more declarative and easier to follow.

```js
// 1) In createGroupActions, replace observeDisplayChange(…) with two event listeners:
if (icon.classList.contains('bb-dockview-control-icon-dropdown')) {
  // bootstrap dropdown events
  icon.addEventListener('show.bs.dropdown',  () => portalPanelIntoDropdown(group));
  icon.addEventListener('hide.bs.dropdown',  () => restorePanelFromDropdown(group));
}
// 2) Move all the move/unwrap logic into two focused functions:

function portalPanelIntoDropdown(group) {
  const panelEle       = group.activePanel.view.content.element;
  const wrapper        = panelEle.parentElement;
  const container      = group.element.querySelector(':scope > .dv-content-container');

  // “portal” the panel into dropdown
  wrapper.style.zIndex = '-1';
  panelEle.__wrapper   = wrapper;            // temp link back to wrapper
  container.appendChild(panelEle);
}

function restorePanelFromDropdown(group) {
  const container = group.element.querySelector(':scope > .dv-content-container');
  Array.from(container.children).forEach(child => {
    const wrapper = child.__wrapper;
    delete child.__wrapper;

    // move child back into its original wrapper, reset z-index
    wrapper.style.zIndex = '';
    wrapper.appendChild(child);
  });

  // re-attach wrappers back into the dockview DOM
  const panels = Array.from(group.element.querySelector(':scope > .dv-content-container').children)
    .map(c => c.__wrapper);
  group.element.parentElement.parentElement.append(...panels);
}

Benefits:

  • No manual MutationObserver or setTimeout
  • Clear semantic hooks (show.bs.dropdown / hide.bs.dropdown)
  • Portal logic lives in two tiny, testable functions

const width = dockview.floatingGroups[index].group.width
fg.position.width = fg.position.width || (width ? width + 2 : 300)
fg.position.height = fg.position.height || dockview.floatingGroups[index].group.height
const group = dockview.floatingGroups[index].group
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Prefer object destructuring when accessing and using properties. (use-object-destructuring)

Suggested change
const group = dockview.floatingGroups[index].group
const {group} = dockview.floatingGroups[index]


ExplanationObject destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct.

From the Airbnb Javascript Style Guide

}
else {
const panelEleList = [...group.element.querySelector('&>.dv-content-container').children].map(item => {
const wrapperEle = item.wrapperEle
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Prefer object destructuring when accessing and using properties. (use-object-destructuring)

Suggested change
const wrapperEle = item.wrapperEle
const {wrapperEle} = item


ExplanationObject destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct.

From the Airbnb Javascript Style Guide

ArgoZhang
ArgoZhang previously approved these changes Jul 6, 2025
@ArgoZhang ArgoZhang changed the title Feat dockview feat(DocView): bump dockview v4.4.0 Jul 6, 2025
@bb-auto bb-auto Bot added the enhancement New feature or request label Jul 6, 2025
@bb-auto bb-auto Bot added this to the v9.2.0 milestone Jul 6, 2025
Co-Authored-By: chengKun <49547008+zhaijunlei955@users.noreply.github.com>
@ArgoZhang ArgoZhang merged commit ecd99ba into master Jul 6, 2025
1 check passed
@ArgoZhang ArgoZhang deleted the feat-dockview branch July 6, 2025 06:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(DocView): bump dockview v4.4.0

2 participants