Skip to content

Commit 26bf850

Browse files
committed
feat: add singular add/destroy panel functions to prevent whole DOM rebuilds
1 parent 04ada7e commit 26bf850

4 files changed

Lines changed: 88 additions & 11 deletions

File tree

src/command/Commands.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,9 @@ define(function (require, exports, module) {
541541
/** Toggles the git panel */
542542
exports.CMD_GIT_TOGGLE_PANEL = "git-toggle-panel";
543543

544+
/** Toggles the custom snippets panel */
545+
exports.CMD_CUSTOM_SNIPPETS_PANEL = "custom_snippets";
546+
544547
/** Goes to next git change */
545548
exports.CMD_GIT_GOTO_NEXT_CHANGE = "git-gotoNextChange";
546549

src/view/DefaultPanelView.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ define(function (require, exports, module) {
5959
id: "snippets",
6060
icon: "fa-solid fa-code",
6161
label: Strings.CUSTOM_SNIPPETS_PANEL_TITLE || "Custom Snippets",
62-
commandID: "custom_snippets"
62+
commandID: Commands.CMD_CUSTOM_SNIPPETS_PANEL
6363
},
6464
{
6565
id: "shortcuts",
@@ -189,8 +189,7 @@ define(function (require, exports, module) {
189189
PanelView.on(PanelView.EVENT_PANEL_SHOWN, function (event, panelID) {
190190
if (panelID !== WorkspaceManager.DEFAULT_PANEL_ID) {
191191
_panel.hide();
192-
}
193-
if (panelID === WorkspaceManager.DEFAULT_PANEL_ID) {
192+
} else {
194193
_updateButtonVisibility();
195194
}
196195
});

src/view/PanelView.js

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,43 @@ define(function (require, exports, module) {
139139
});
140140
}
141141

142+
/**
143+
* Append a single tab to the tab bar for the given panel.
144+
* Use instead of _updateBottomPanelTabBar() when adding one tab.
145+
* @param {string} panelId
146+
* @private
147+
*/
148+
function _addTabToBar(panelId) {
149+
if (!_$tabsOverflow) {
150+
return;
151+
}
152+
let panel = _panelMap[panelId];
153+
if (!panel) {
154+
return;
155+
}
156+
let title = panel._tabTitle || _getPanelTitle(panelId, panel.$panel);
157+
let isActive = (panelId === _activeId);
158+
let $tab = $('<div class="bottom-panel-tab"></div>')
159+
.toggleClass('active', isActive)
160+
.attr('data-panel-id', panelId);
161+
$tab.append($('<span class="bottom-panel-tab-title"></span>').text(title));
162+
$tab.append($('<span class="bottom-panel-tab-close-btn">&times;</span>').attr('title', Strings.CLOSE));
163+
_$tabsOverflow.append($tab);
164+
}
165+
166+
/**
167+
* Remove a single tab from the tab bar by panel ID.
168+
* Use instead of _updateBottomPanelTabBar() when removing one tab.
169+
* @param {string} panelId
170+
* @private
171+
*/
172+
function _removeTabFromBar(panelId) {
173+
if (!_$tabsOverflow) {
174+
return;
175+
}
176+
_$tabsOverflow.find('.bottom-panel-tab[data-panel-id="' + panelId + '"]').remove();
177+
}
178+
142179
/**
143180
* Switch the active tab to the given panel. Does not show/hide the container.
144181
* @param {string} panelId
@@ -223,7 +260,7 @@ define(function (require, exports, module) {
223260
* Shows the panel
224261
*/
225262
Panel.prototype.show = function () {
226-
if (!this.canBeShown()) {
263+
if (!this.canBeShown() || !_$container) {
227264
return;
228265
}
229266
let panelId = this.panelID;
@@ -256,7 +293,7 @@ define(function (require, exports, module) {
256293
}
257294

258295
_switchToTab(panelId);
259-
_updateBottomPanelTabBar();
296+
_addTabToBar(panelId);
260297
exports.trigger(EVENT_PANEL_SHOWN, panelId);
261298
};
262299

@@ -276,22 +313,30 @@ define(function (require, exports, module) {
276313
this.$panel.removeClass("active-bottom-panel");
277314

278315
let wasActive = (_activeId === panelId);
316+
let activatedId = null;
279317

280-
// Tab was removed — rebuild tab bar, then activate next if needed
281318
if (wasActive && _openIds.length > 0) {
282319
let nextIdx = Math.min(idx, _openIds.length - 1);
283-
let nextId = _openIds[nextIdx];
320+
activatedId = _openIds[nextIdx];
284321
_activeId = null; // clear so _switchToTab runs
285-
_switchToTab(nextId);
286-
exports.trigger(EVENT_PANEL_SHOWN, nextId);
322+
_switchToTab(activatedId);
287323
} else if (wasActive) {
288324
// No more tabs - hide the container
289325
_activeId = null;
290-
Resizer.hide(_$container[0]);
326+
if (_$container) {
327+
Resizer.hide(_$container[0]);
328+
}
291329
}
292-
_updateBottomPanelTabBar();
293330

331+
_removeTabFromBar(panelId);
332+
333+
// Always fire HIDDEN for the closed panel first
294334
exports.trigger(EVENT_PANEL_HIDDEN, panelId);
335+
336+
// Then fire SHOWN for the newly activated tab, if any
337+
if (activatedId) {
338+
exports.trigger(EVENT_PANEL_SHOWN, activatedId);
339+
}
295340
};
296341

297342
/**
@@ -318,6 +363,18 @@ define(function (require, exports, module) {
318363
}
319364
};
320365

366+
/**
367+
* Destroys the panel, removing it from the tab bar, internal maps, and the DOM.
368+
* After calling this, the Panel instance should not be reused.
369+
*/
370+
Panel.prototype.destroy = function () {
371+
if (_openIds.indexOf(this.panelID) !== -1) {
372+
this.hide();
373+
}
374+
delete _panelMap[this.panelID];
375+
this.$panel.remove();
376+
};
377+
321378
/**
322379
* gets the Panel's type
323380
* @return {string}

src/view/WorkspaceManager.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,23 @@ define(function (require, exports, module) {
262262
return bottomPanel;
263263
}
264264

265+
/**
266+
* Destroys a bottom panel, removing it from internal registries, the tab bar, and the DOM.
267+
* After calling this, the panel ID is no longer valid and the Panel instance should not be reused.
268+
*
269+
* @param {!string} id The panel ID that was passed to createBottomPanel.
270+
*/
271+
function destroyBottomPanel(id) {
272+
let panel = panelIDMap[id];
273+
if (!panel) {
274+
return;
275+
}
276+
if (typeof panel.destroy === 'function') {
277+
panel.destroy();
278+
}
279+
delete panelIDMap[id];
280+
}
281+
265282
/**
266283
* Creates a new resizable plugin panel associated with the given toolbar icon. Panel is initially invisible.
267284
* The panel's size & visibility are automatically saved & restored. Only one panel can be associated with a
@@ -632,6 +649,7 @@ define(function (require, exports, module) {
632649

633650
// Define public API
634651
exports.createBottomPanel = createBottomPanel;
652+
exports.destroyBottomPanel = destroyBottomPanel;
635653
exports.createPluginPanel = createPluginPanel;
636654
exports.isPanelVisible = isPanelVisible;
637655
exports.setPluginPanelWidth = setPluginPanelWidth;

0 commit comments

Comments
 (0)