diff --git a/packages/widgets/src/docklayout.ts b/packages/widgets/src/docklayout.ts
index 6d96c9950..c35e51d78 100644
--- a/packages/widgets/src/docklayout.ts
+++ b/packages/widgets/src/docklayout.ts
@@ -852,18 +852,21 @@ export class DockLayout extends Layout {
// Using transform create an additional layer in the pixel pipeline
// to limit the number of layer, it is set only if there is more than one widget.
- if (
- this._hiddenMode === Widget.HiddenMode.Scale &&
- refNode.tabBar.titles.length > 0
- ) {
- if (refNode.tabBar.titles.length == 1) {
+ if (this._hiddenMode === Widget.HiddenMode.Scale) {
+ if (refNode.tabBar.titles.length === 0) {
+ // Singular tab should use display mode to limit number of layers.
+ widget.hiddenMode = Widget.HiddenMode.Display;
+ } else if (refNode.tabBar.titles.length == 1) {
+ // If we are adding a second tab, switch the existing tab back to scale.
const existingWidget = refNode.tabBar.titles[0].owner;
existingWidget.hiddenMode = Widget.HiddenMode.Scale;
+ } else {
+ // For the third and subsequent tabs no special action is needed.
+ widget.hiddenMode = Widget.HiddenMode.Scale;
}
-
- widget.hiddenMode = Widget.HiddenMode.Scale;
} else {
- widget.hiddenMode = Widget.HiddenMode.Display;
+ // For all other modes just propagate the current mode.
+ widget.hiddenMode = this._hiddenMode;
}
// Insert the widget's tab relative to the target index.
diff --git a/packages/widgets/src/widget.ts b/packages/widgets/src/widget.ts
index f07d73e24..f2ae230c4 100644
--- a/packages/widgets/src/widget.ts
+++ b/packages/widgets/src/widget.ts
@@ -183,30 +183,23 @@ export class Widget implements IMessageHandler, IObservableDisposable {
if (this._hiddenMode === value) {
return;
}
- this._hiddenMode = value;
- switch (value) {
- case Widget.HiddenMode.Display:
- this.node.style.willChange = 'auto';
- break;
- case Widget.HiddenMode.Scale:
- this.node.style.willChange = 'transform';
- break;
+
+ if (this.isHidden) {
+ // Reset styles set by previous mode.
+ this._toggleHidden(false);
}
+ if (value == Widget.HiddenMode.Scale) {
+ this.node.style.willChange = 'transform';
+ } else {
+ this.node.style.willChange = 'auto';
+ }
+
+ this._hiddenMode = value;
+
if (this.isHidden) {
- if (value === Widget.HiddenMode.Display) {
- this.addClass('lm-mod-hidden');
- /* */
- this.addClass('p-mod-hidden');
- /* */
- this.node.style.transform = '';
- } else {
- this.node.style.transform = 'scale(0)';
- this.removeClass('lm-mod-hidden');
- /* */
- this.removeClass('p-mod-hidden');
- /* */
- }
+ // Set styles for new mode.
+ this._toggleHidden(true);
}
}
@@ -434,14 +427,7 @@ export class Widget implements IMessageHandler, IObservableDisposable {
}
this.clearFlag(Widget.Flag.IsHidden);
this.node.removeAttribute('aria-hidden');
- if (this.hiddenMode === Widget.HiddenMode.Display) {
- this.removeClass('lm-mod-hidden');
- /* */
- this.removeClass('p-mod-hidden');
- /* */
- } else {
- this.node.style.transform = '';
- }
+ this._toggleHidden(false);
if (this.isAttached && (!this.parent || this.parent.isVisible)) {
MessageLoop.sendMessage(this, Widget.Msg.AfterShow);
@@ -469,14 +455,7 @@ export class Widget implements IMessageHandler, IObservableDisposable {
}
this.setFlag(Widget.Flag.IsHidden);
this.node.setAttribute('aria-hidden', 'true');
- if (this.hiddenMode === Widget.HiddenMode.Display) {
- this.addClass('lm-mod-hidden');
- /* */
- this.addClass('p-mod-hidden');
- /* */
- } else {
- this.node.style.transform = 'scale(0)';
- }
+ this._toggleHidden(true);
if (this.isAttached && (!this.parent || this.parent.isVisible)) {
MessageLoop.sendMessage(this, Widget.Msg.AfterHide);
@@ -759,6 +738,42 @@ export class Widget implements IMessageHandler, IObservableDisposable {
*/
protected onChildRemoved(msg: Widget.ChildMessage): void {}
+ private _toggleHidden(hidden: boolean) {
+ if (hidden) {
+ switch (this._hiddenMode) {
+ case Widget.HiddenMode.Display:
+ this.addClass('lm-mod-hidden');
+ /* */
+ this.addClass('p-mod-hidden');
+ /* */
+ break;
+ case Widget.HiddenMode.Scale:
+ this.node.style.transform = 'scale(0)';
+ break;
+ case Widget.HiddenMode.ContentVisibility:
+ this.node.style.contentVisibility = 'hidden';
+ this.node.style.zIndex = '-1';
+ break;
+ }
+ } else {
+ switch (this._hiddenMode) {
+ case Widget.HiddenMode.Display:
+ this.removeClass('lm-mod-hidden');
+ /* */
+ this.removeClass('p-mod-hidden');
+ /* */
+ break;
+ case Widget.HiddenMode.Scale:
+ this.node.style.transform = '';
+ break;
+ case Widget.HiddenMode.ContentVisibility:
+ this.node.style.contentVisibility = '';
+ this.node.style.zIndex = '';
+ break;
+ }
+ }
+ }
+
private _flags = 0;
private _layout: Layout | null = null;
private _parent: Widget | null = null;
@@ -819,7 +834,12 @@ export namespace Widget {
/**
* Hide the widget by setting the `transform` to `'scale(0)'`.
*/
- Scale
+ Scale,
+
+ /**
+ *Hide the widget by setting the `content-visibility` to `'hidden'`.
+ */
+ ContentVisibility
}
/**
diff --git a/packages/widgets/tests/src/widget.spec.ts b/packages/widgets/tests/src/widget.spec.ts
index 82e6c750f..9bca55346 100644
--- a/packages/widgets/tests/src/widget.spec.ts
+++ b/packages/widgets/tests/src/widget.spec.ts
@@ -740,10 +740,38 @@ describe('@lumino/widgets', () => {
widget.dispose();
});
- it('should add class when switching from scale to display', () => {
+ for (const fromMode of [
+ Widget.HiddenMode.Scale,
+ Widget.HiddenMode.ContentVisibility
+ ]) {
+ it(`should add class when switching from ${fromMode} to display`, () => {
+ let widget = new Widget();
+ Widget.attach(widget, document.body);
+ widget.hiddenMode = fromMode;
+ widget.hide();
+ widget.hiddenMode = Widget.HiddenMode.Display;
+ expect(widget.hasClass('lm-mod-hidden')).to.equal(true);
+ expect(widget.node.style.transform).to.equal('');
+ expect(widget.node.style.willChange).to.equal('auto');
+ widget.dispose();
+ });
+ }
+
+ it('should use content-visibility in relevant mode', () => {
let widget = new Widget();
Widget.attach(widget, document.body);
- widget.hiddenMode = Widget.HiddenMode.Scale;
+ widget.hiddenMode = Widget.HiddenMode.ContentVisibility;
+ widget.hide();
+ expect(widget.hasClass('lm-mod-hidden')).to.equal(false);
+ expect(widget.node.style.contentVisibility).to.equal('hidden');
+ expect(widget.node.style.willChange).to.equal('auto');
+ widget.dispose();
+ });
+
+ it('should add class when switching from content-visibility to display', () => {
+ let widget = new Widget();
+ Widget.attach(widget, document.body);
+ widget.hiddenMode = Widget.HiddenMode.ContentVisibility;
widget.hide();
widget.hiddenMode = Widget.HiddenMode.Display;
expect(widget.hasClass('lm-mod-hidden')).to.equal(true);
diff --git a/review/api/widgets.api.md b/review/api/widgets.api.md
index 07b9ca32f..b3a24ba48 100644
--- a/review/api/widgets.api.md
+++ b/review/api/widgets.api.md
@@ -1267,6 +1267,7 @@ export namespace Widget {
IsVisible = 8
}
export enum HiddenMode {
+ ContentVisibility = 2,
Display = 0,
Scale = 1
}