dockview 1.8.3 → 1.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * dockview
3
- * @version 1.8.3
3
+ * @version 1.8.4
4
4
  * @link https://github.com/mathuo/dockview
5
5
  * @license MIT
6
6
  */
@@ -899,7 +899,17 @@ class Splitview {
899
899
  size = typeof size === 'number' ? size : item.size;
900
900
  size = clamp(size, item.minimumSize, item.maximumSize);
901
901
  item.size = size;
902
- this.relayout([index]);
902
+ const indexes = range(this.viewItems.length).filter((i) => i !== index);
903
+ const lowPriorityIndexes = [
904
+ ...indexes.filter((i) => this.viewItems[i].priority === LayoutPriority.Low),
905
+ index,
906
+ ];
907
+ const highPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === LayoutPriority.High);
908
+ /**
909
+ * add this view we are changing to the low-index list since we have determined the size
910
+ * here and don't want it changed
911
+ */
912
+ this.relayout([...lowPriorityIndexes, index], highPriorityIndexes);
903
913
  }
904
914
  addView(view, size = { type: 'distribute' }, index = this.viewItems.length, skipLayout) {
905
915
  const container = document.createElement('div');
@@ -1656,6 +1666,7 @@ class BranchNode extends CompositeDisposable {
1656
1666
  orientation: this.orientation,
1657
1667
  descriptor,
1658
1668
  proportionalLayout,
1669
+ styles,
1659
1670
  });
1660
1671
  }
1661
1672
  this.addDisposables(this._onDidChange, this.splitview.onDidSashEnd(() => {
@@ -2167,6 +2178,14 @@ class Gridview {
2167
2178
  const child = sibling.children[i];
2168
2179
  grandParent.addChild(child, child.size, parentIndex + i);
2169
2180
  }
2181
+ /**
2182
+ * clean down the branch node since we need to dipose of it and
2183
+ * when .dispose() it called on a branch it will dispose of any
2184
+ * views it is holding onto.
2185
+ */
2186
+ while (sibling.children.length > 0) {
2187
+ sibling.removeChild(0);
2188
+ }
2170
2189
  }
2171
2190
  else {
2172
2191
  // otherwise create a new leaf node and add that to the grandparent
@@ -3225,6 +3244,19 @@ class TabsContainer extends CompositeDisposable {
3225
3244
  this.leftActions = element;
3226
3245
  }
3227
3246
  }
3247
+ setPrefixActionsElement(element) {
3248
+ if (this.preActions === element) {
3249
+ return;
3250
+ }
3251
+ if (this.preActions) {
3252
+ this.preActions.remove();
3253
+ this.preActions = undefined;
3254
+ }
3255
+ if (element) {
3256
+ this.preActionsContainer.appendChild(element);
3257
+ this.preActions = element;
3258
+ }
3259
+ }
3228
3260
  get element() {
3229
3261
  return this._element;
3230
3262
  }
@@ -3265,9 +3297,12 @@ class TabsContainer extends CompositeDisposable {
3265
3297
  this.rightActionsContainer.className = 'right-actions-container';
3266
3298
  this.leftActionsContainer = document.createElement('div');
3267
3299
  this.leftActionsContainer.className = 'left-actions-container';
3300
+ this.preActionsContainer = document.createElement('div');
3301
+ this.preActionsContainer.className = 'pre-actions-container';
3268
3302
  this.tabContainer = document.createElement('div');
3269
3303
  this.tabContainer.className = 'tabs-container';
3270
3304
  this.voidContainer = new VoidContainer(this.accessor, this.group);
3305
+ this._element.appendChild(this.preActionsContainer);
3271
3306
  this._element.appendChild(this.tabContainer);
3272
3307
  this._element.appendChild(this.leftActionsContainer);
3273
3308
  this._element.appendChild(this.voidContainer.element);
@@ -3557,6 +3592,16 @@ class DockviewGroupPanelModel extends CompositeDisposable {
3557
3592
  });
3558
3593
  this.tabsContainer.setLeftActionsElement(this._leftHeaderActions.element);
3559
3594
  }
3595
+ if (this.accessor.options.createPrefixHeaderActionsElement) {
3596
+ this._prefixHeaderActions =
3597
+ this.accessor.options.createPrefixHeaderActionsElement(this.groupPanel);
3598
+ this.addDisposables(this._prefixHeaderActions);
3599
+ this._prefixHeaderActions.init({
3600
+ containerApi: new DockviewApi(this.accessor),
3601
+ api: this.groupPanel.api,
3602
+ });
3603
+ this.tabsContainer.setPrefixActionsElement(this._prefixHeaderActions.element);
3604
+ }
3560
3605
  }
3561
3606
  indexOf(panel) {
3562
3607
  return this.tabsContainer.indexOf(panel.id);
@@ -3899,6 +3944,14 @@ class Resizable extends CompositeDisposable {
3899
3944
  */
3900
3945
  return;
3901
3946
  }
3947
+ if (!document.body.contains(this._element)) {
3948
+ /**
3949
+ * since the event is dispatched through requestAnimationFrame there is a small chance
3950
+ * the component is no longer attached to the DOM, if that is the case the dimensions
3951
+ * are mostly likely all zero and meaningless. we should skip this case.
3952
+ */
3953
+ return;
3954
+ }
3902
3955
  const { width, height } = entry.contentRect;
3903
3956
  this.layout(width, height);
3904
3957
  }));
@@ -5927,61 +5980,114 @@ class DockviewComponent extends BaseGrid {
5927
5980
  fromJSON(data) {
5928
5981
  var _a;
5929
5982
  this.clear();
5983
+ if (typeof data !== 'object' || data === null) {
5984
+ throw new Error('serialized layout must be a non-null object');
5985
+ }
5930
5986
  const { grid, panels, activeGroup } = data;
5931
5987
  if (grid.root.type !== 'branch' || !Array.isArray(grid.root.data)) {
5932
5988
  throw new Error('root must be of type branch');
5933
5989
  }
5934
- // take note of the existing dimensions
5935
- const width = this.width;
5936
- const height = this.height;
5937
- const createGroupFromSerializedState = (data) => {
5938
- const { id, locked, hideHeader, views, activeView } = data;
5939
- const group = this.createGroup({
5940
- id,
5941
- locked: !!locked,
5942
- hideHeader: !!hideHeader,
5943
- });
5944
- this._onDidAddGroup.fire(group);
5945
- for (const child of views) {
5946
- const panel = this._deserializer.fromJSON(panels[child], group);
5947
- const isActive = typeof activeView === 'string' && activeView === panel.id;
5948
- group.model.openPanel(panel, {
5949
- skipSetPanelActive: !isActive,
5950
- skipSetGroupActive: true,
5990
+ try {
5991
+ // take note of the existing dimensions
5992
+ const width = this.width;
5993
+ const height = this.height;
5994
+ const createGroupFromSerializedState = (data) => {
5995
+ const { id, locked, hideHeader, views, activeView } = data;
5996
+ if (typeof id !== 'string') {
5997
+ throw new Error('group id must be of type string');
5998
+ }
5999
+ const group = this.createGroup({
6000
+ id,
6001
+ locked: !!locked,
6002
+ hideHeader: !!hideHeader,
5951
6003
  });
6004
+ const createdPanels = [];
6005
+ for (const child of views) {
6006
+ /**
6007
+ * Run the deserializer step seperately since this may fail to due corrupted external state.
6008
+ * In running this section first we avoid firing lots of 'add' events in the event of a failure
6009
+ * due to a corruption of input data.
6010
+ */
6011
+ const panel = this._deserializer.fromJSON(panels[child], group);
6012
+ createdPanels.push(panel);
6013
+ }
6014
+ this._onDidAddGroup.fire(group);
6015
+ for (let i = 0; i < views.length; i++) {
6016
+ const panel = createdPanels[i];
6017
+ const isActive = typeof activeView === 'string' &&
6018
+ activeView === panel.id;
6019
+ group.model.openPanel(panel, {
6020
+ skipSetPanelActive: !isActive,
6021
+ skipSetGroupActive: true,
6022
+ });
6023
+ }
6024
+ if (!group.activePanel && group.panels.length > 0) {
6025
+ group.model.openPanel(group.panels[group.panels.length - 1], {
6026
+ skipSetGroupActive: true,
6027
+ });
6028
+ }
6029
+ return group;
6030
+ };
6031
+ this.gridview.deserialize(grid, {
6032
+ fromJSON: (node) => {
6033
+ return createGroupFromSerializedState(node.data);
6034
+ },
6035
+ });
6036
+ this.layout(width, height, true);
6037
+ const serializedFloatingGroups = (_a = data.floatingGroups) !== null && _a !== void 0 ? _a : [];
6038
+ for (const serializedFloatingGroup of serializedFloatingGroups) {
6039
+ const { data, position } = serializedFloatingGroup;
6040
+ const group = createGroupFromSerializedState(data);
6041
+ this.addFloatingGroup(group, {
6042
+ x: position.left,
6043
+ y: position.top,
6044
+ height: position.height,
6045
+ width: position.width,
6046
+ }, { skipRemoveGroup: true, inDragMode: false });
6047
+ }
6048
+ for (const floatingGroup of this.floatingGroups) {
6049
+ floatingGroup.overlay.setBounds();
6050
+ }
6051
+ if (typeof activeGroup === 'string') {
6052
+ const panel = this.getPanel(activeGroup);
6053
+ if (panel) {
6054
+ this.doSetGroupActive(panel);
6055
+ }
5952
6056
  }
5953
- if (!group.activePanel && group.panels.length > 0) {
5954
- group.model.openPanel(group.panels[group.panels.length - 1], {
5955
- skipSetGroupActive: true,
5956
- });
6057
+ }
6058
+ catch (err) {
6059
+ /**
6060
+ * Takes all the successfully created groups and remove all of their panels.
6061
+ */
6062
+ for (const group of this.groups) {
6063
+ for (const panel of group.panels) {
6064
+ this.removePanel(panel, {
6065
+ removeEmptyGroup: false,
6066
+ skipDispose: false,
6067
+ });
6068
+ }
5957
6069
  }
5958
- return group;
5959
- };
5960
- this.gridview.deserialize(grid, {
5961
- fromJSON: (node) => {
5962
- return createGroupFromSerializedState(node.data);
5963
- },
5964
- });
5965
- this.layout(width, height, true);
5966
- const serializedFloatingGroups = (_a = data.floatingGroups) !== null && _a !== void 0 ? _a : [];
5967
- for (const serializedFloatingGroup of serializedFloatingGroups) {
5968
- const { data, position } = serializedFloatingGroup;
5969
- const group = createGroupFromSerializedState(data);
5970
- this.addFloatingGroup(group, {
5971
- x: position.left,
5972
- y: position.top,
5973
- height: position.height,
5974
- width: position.width,
5975
- }, { skipRemoveGroup: true, inDragMode: false });
5976
- }
5977
- for (const floatingGroup of this.floatingGroups) {
5978
- floatingGroup.overlay.setBounds();
5979
- }
5980
- if (typeof activeGroup === 'string') {
5981
- const panel = this.getPanel(activeGroup);
5982
- if (panel) {
5983
- this.doSetGroupActive(panel);
6070
+ /**
6071
+ * To remove a group we cannot call this.removeGroup(...) since this makes assumptions about
6072
+ * the underlying HTMLElement existing in the Gridview.
6073
+ */
6074
+ for (const group of this.groups) {
6075
+ group.dispose();
6076
+ this._groups.delete(group.id);
6077
+ this._onDidRemoveGroup.fire(group);
5984
6078
  }
6079
+ // iterate over a reassigned array since original array will be modified
6080
+ for (const floatingGroup of [...this.floatingGroups]) {
6081
+ floatingGroup.dispose();
6082
+ }
6083
+ // fires clean-up events and clears the underlying HTML gridview.
6084
+ this.clear();
6085
+ /**
6086
+ * even though we have cleaned-up we still want to inform the caller of their error
6087
+ * and we'll do this through re-throwing the original error since afterall you would
6088
+ * expect trying to load a corrupted layout to result in an error and not silently fail...
6089
+ */
6090
+ throw err;
5985
6091
  }
5986
6092
  this._onDidLayoutFromJSON.fire();
5987
6093
  }
@@ -6195,6 +6301,7 @@ class DockviewComponent extends BaseGrid {
6195
6301
  if (!(options === null || options === void 0 ? void 0 : options.skipDispose)) {
6196
6302
  floatingGroup.group.dispose();
6197
6303
  this._groups.delete(group.id);
6304
+ // TODO: fire group removed event?
6198
6305
  }
6199
6306
  floatingGroup.dispose();
6200
6307
  return floatingGroup.group;
@@ -6435,43 +6542,64 @@ class GridviewComponent extends BaseGrid {
6435
6542
  fromJSON(serializedGridview) {
6436
6543
  this.clear();
6437
6544
  const { grid, activePanel } = serializedGridview;
6438
- const queue = [];
6439
- // take note of the existing dimensions
6440
- const width = this.width;
6441
- const height = this.height;
6442
- this.gridview.deserialize(grid, {
6443
- fromJSON: (node) => {
6444
- const { data } = node;
6445
- const view = createComponent(data.id, data.component, this.options.components || {}, this.options.frameworkComponents || {}, this.options.frameworkComponentFactory
6446
- ? {
6447
- createComponent: this.options.frameworkComponentFactory
6448
- .createComponent,
6449
- }
6450
- : undefined);
6451
- queue.push(() => view.init({
6452
- params: data.params,
6453
- minimumWidth: data.minimumWidth,
6454
- maximumWidth: data.maximumWidth,
6455
- minimumHeight: data.minimumHeight,
6456
- maximumHeight: data.maximumHeight,
6457
- priority: data.priority,
6458
- snap: !!data.snap,
6459
- accessor: this,
6460
- isVisible: node.visible,
6461
- }));
6462
- this._onDidAddGroup.fire(view);
6463
- this.registerPanel(view);
6464
- return view;
6465
- },
6466
- });
6467
- this.layout(width, height, true);
6468
- queue.forEach((f) => f());
6469
- if (typeof activePanel === 'string') {
6470
- const panel = this.getPanel(activePanel);
6471
- if (panel) {
6472
- this.doSetGroupActive(panel);
6545
+ try {
6546
+ const queue = [];
6547
+ // take note of the existing dimensions
6548
+ const width = this.width;
6549
+ const height = this.height;
6550
+ this.gridview.deserialize(grid, {
6551
+ fromJSON: (node) => {
6552
+ const { data } = node;
6553
+ const view = createComponent(data.id, data.component, this.options.components || {}, this.options.frameworkComponents || {}, this.options.frameworkComponentFactory
6554
+ ? {
6555
+ createComponent: this.options.frameworkComponentFactory
6556
+ .createComponent,
6557
+ }
6558
+ : undefined);
6559
+ queue.push(() => view.init({
6560
+ params: data.params,
6561
+ minimumWidth: data.minimumWidth,
6562
+ maximumWidth: data.maximumWidth,
6563
+ minimumHeight: data.minimumHeight,
6564
+ maximumHeight: data.maximumHeight,
6565
+ priority: data.priority,
6566
+ snap: !!data.snap,
6567
+ accessor: this,
6568
+ isVisible: node.visible,
6569
+ }));
6570
+ this._onDidAddGroup.fire(view);
6571
+ this.registerPanel(view);
6572
+ return view;
6573
+ },
6574
+ });
6575
+ this.layout(width, height, true);
6576
+ queue.forEach((f) => f());
6577
+ if (typeof activePanel === 'string') {
6578
+ const panel = this.getPanel(activePanel);
6579
+ if (panel) {
6580
+ this.doSetGroupActive(panel);
6581
+ }
6473
6582
  }
6474
6583
  }
6584
+ catch (err) {
6585
+ /**
6586
+ * To remove a group we cannot call this.removeGroup(...) since this makes assumptions about
6587
+ * the underlying HTMLElement existing in the Gridview.
6588
+ */
6589
+ for (const group of this.groups) {
6590
+ group.dispose();
6591
+ this._groups.delete(group.id);
6592
+ this._onDidRemoveGroup.fire(group);
6593
+ }
6594
+ // fires clean-up events and clears the underlying HTML gridview.
6595
+ this.clear();
6596
+ /**
6597
+ * even though we have cleaned-up we still want to inform the caller of their error
6598
+ * and we'll do this through re-throwing the original error since afterall you would
6599
+ * expect trying to load a corrupted layout to result in an error and not silently fail...
6600
+ */
6601
+ throw err;
6602
+ }
6475
6603
  this._onDidLayoutfromJSON.fire();
6476
6604
  }
6477
6605
  clear() {
@@ -7655,6 +7783,7 @@ const DockviewReact = React.forwardRef((props, ref) => {
7655
7783
  showDndOverlay: props.showDndOverlay,
7656
7784
  createLeftHeaderActionsElement: createGroupControlElement(props.leftHeaderActionsComponent, { addPortal }),
7657
7785
  createRightHeaderActionsElement: createGroupControlElement(props.rightHeaderActionsComponent, { addPortal }),
7786
+ createPrefixHeaderActionsElement: createGroupControlElement(props.prefixHeaderActionsComponent, { addPortal }),
7658
7787
  singleTabMode: props.singleTabMode,
7659
7788
  disableFloatingGroups: props.disableFloatingGroups,
7660
7789
  floatingGroupBounds: props.floatingGroupBounds,
@@ -7764,6 +7893,14 @@ const DockviewReact = React.forwardRef((props, ref) => {
7764
7893
  createLeftHeaderActionsElement: createGroupControlElement(props.leftHeaderActionsComponent, { addPortal }),
7765
7894
  });
7766
7895
  }, [props.leftHeaderActionsComponent]);
7896
+ React.useEffect(() => {
7897
+ if (!dockviewRef.current) {
7898
+ return;
7899
+ }
7900
+ dockviewRef.current.updateOptions({
7901
+ createPrefixHeaderActionsElement: createGroupControlElement(props.prefixHeaderActionsComponent, { addPortal }),
7902
+ });
7903
+ }, [props.prefixHeaderActionsComponent]);
7767
7904
  return (React.createElement("div", { className: props.className, style: { height: '100%', width: '100%' }, ref: domRef }, portals));
7768
7905
  });
7769
7906
  DockviewReact.displayName = 'DockviewComponent';