View Context: observe parent activation to make sure children follows along. (#20206)

observe parent activation to make sure children follows along.
This commit is contained in:
Niels Lyngsø
2025-09-20 14:03:41 +02:00
committed by GitHub
parent 37f9dea259
commit fa575d1f84

View File

@@ -1,5 +1,10 @@
import { UMB_VIEW_CONTEXT } from './view.context-token.js'; import { UMB_VIEW_CONTEXT } from './view.context-token.js';
import { UmbClassState, UmbStringState, mergeObservables } from '@umbraco-cms/backoffice/observable-api'; import {
UmbBooleanState,
UmbClassState,
UmbStringState,
mergeObservables,
} from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { UmbHintController } from '@umbraco-cms/backoffice/hint'; import { UmbHintController } from '@umbraco-cms/backoffice/hint';
import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api';
@@ -9,6 +14,8 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type { UmbVariantHint } from '@umbraco-cms/backoffice/hint'; import type { UmbVariantHint } from '@umbraco-cms/backoffice/hint';
import type { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import type { UmbVariantId } from '@umbraco-cms/backoffice/variant';
const ObserveParentActiveCtrlAlias = Symbol();
/** /**
* *
* TODO: * TODO:
@@ -30,7 +37,11 @@ export class UmbViewController extends UmbControllerBase {
// State used to know if the context can be auto activated when attached. // State used to know if the context can be auto activated when attached.
#autoActivate = true; #autoActivate = true;
#active = false; #active = new UmbBooleanState(false);
public readonly active = this.#active.asObservable();
get isActive() {
return this.#active.getValue();
}
#hasActiveChild = false; #hasActiveChild = false;
#inherit?: boolean; #inherit?: boolean;
#explicitInheritance?: boolean; #explicitInheritance?: boolean;
@@ -68,11 +79,11 @@ export class UmbViewController extends UmbControllerBase {
this.#consumeParentCtrl = this.consumeContext(UMB_VIEW_CONTEXT, (parentView) => { this.#consumeParentCtrl = this.consumeContext(UMB_VIEW_CONTEXT, (parentView) => {
// In case of explicit inheritance we do not want to overview the parent view. // In case of explicit inheritance we do not want to overview the parent view.
if (this.#explicitInheritance) return; if (this.#explicitInheritance) return;
if (this.#active && !this.#hasActiveChild) { if (this.isActive && !this.#hasActiveChild) {
// If we were active we will react as if we got deactivated and then activated again below if state allows. [NL] // If we were active we will react as if we got deactivated and then activated again below if state allows. [NL]
this.#propagateActivation(); this.#propagateActivation();
} }
this.#active = false; this.#active.setValue(false);
if (parentView) { if (parentView) {
this.#parentView = parentView; this.#parentView = parentView;
} }
@@ -125,22 +136,23 @@ export class UmbViewController extends UmbControllerBase {
} }
override hostConnected(): void { override hostConnected(): void {
const wasActive = this.isActive;
this.#attached = true; this.#attached = true;
super.hostConnected(); super.hostConnected();
// CHeck that we have a providerController, otherwise this is not provided. [NL] // Check that we have a providerController, otherwise this is not provided. [NL]
if (this.#autoActivate) { if (this.#autoActivate && !wasActive) {
this._internal_activate(); this._internal_activate();
} }
} }
override hostDisconnected(): void { override hostDisconnected(): void {
const wasAttached = this.#attached; const wasAttached = this.#attached;
const wasActive = this.#active; const wasActive = this.isActive;
this.#attached = false; this.#attached = false;
this.#active = false; this.#active.setValue(false);
super.hostDisconnected(); super.hostDisconnected();
if (wasAttached === true && wasActive) { if (wasAttached === true && wasActive) {
// CHeck that we have a providerController, otherwise this is not provided. [NL] // Check that we have a providerController, otherwise this is not provided. [NL]
this.#propagateActivation(); this.#propagateActivation();
} }
} }
@@ -155,6 +167,18 @@ export class UmbViewController extends UmbControllerBase {
this.#consumeParentCtrl?.destroy(); this.#consumeParentCtrl?.destroy();
this.#consumeParentCtrl = undefined; this.#consumeParentCtrl = undefined;
this.#parentView = context; this.#parentView = context;
// Notice because we cannot break the inheritance, we do not need to stop this observation in any of the logic. [NL]
this.observe(
this.#parentView?.active,
(isActive) => {
if (isActive) {
this._internal_activate();
} else {
this._internal_deactivate();
}
},
ObserveParentActiveCtrlAlias,
);
this.#inheritFromParent(); this.#inheritFromParent();
this.#propagateActivation(); this.#propagateActivation();
} }
@@ -172,7 +196,7 @@ export class UmbViewController extends UmbControllerBase {
() => { () => {
this.#computeTitle(); this.#computeTitle();
// Check for parent view as it is undefined in a disassembling state and we do not want to update the title in that situation. [NL] // Check for parent view as it is undefined in a disassembling state and we do not want to update the title in that situation. [NL]
if (this.#providerCtrl && this.#parentView && this.#active) { if (this.#providerCtrl && this.#parentView && this.isActive) {
this.#updateTitle(); this.#updateTitle();
} }
}, },
@@ -184,13 +208,13 @@ export class UmbViewController extends UmbControllerBase {
#propagateActivation() { #propagateActivation() {
if (!this.#parentView) return; if (!this.#parentView) return;
if (this.#inherit) { if (this.#inherit) {
if (this.#active) { if (this.isActive) {
this.#parentView._internal_childActivated(); this.#parentView._internal_childActivated();
} else { } else {
this.#parentView._internal_childDeactivated(); this.#parentView._internal_childDeactivated();
} }
} else { } else {
if (this.#active) { if (this.isActive) {
this.#parentView._internal_deactivate(); this.#parentView._internal_deactivate();
} else { } else {
this.#parentView._internal_activate(); this.#parentView._internal_activate();
@@ -209,7 +233,7 @@ export class UmbViewController extends UmbControllerBase {
return; return;
} }
this.#autoActivate = true; this.#autoActivate = true;
if (this.#active === true) { if (this.isActive) {
return; return;
} }
// If not attached then propagate the activation to the parent. [NL] // If not attached then propagate the activation to the parent. [NL]
@@ -219,7 +243,7 @@ export class UmbViewController extends UmbControllerBase {
} }
this.#propagateActivation(); this.#propagateActivation();
} else { } else {
this.#active = true; this.#active.setValue(true);
this.#propagateActivation(); this.#propagateActivation();
this.#updateTitle(); this.#updateTitle();
// TODO: Start shortcuts. [NL] // TODO: Start shortcuts. [NL]
@@ -266,8 +290,8 @@ export class UmbViewController extends UmbControllerBase {
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
public _internal_deactivate() { public _internal_deactivate() {
this.#autoActivate = false; this.#autoActivate = false;
if (!this.#active) return; if (!this.isActive) return;
this.#active = false; this.#active.setValue(false);
// TODO: Stop shortcuts. [NL] // TODO: Stop shortcuts. [NL]
// Deactivate parents: // Deactivate parents:
this.#propagateActivation(); this.#propagateActivation();
@@ -298,7 +322,7 @@ export class UmbViewController extends UmbControllerBase {
override destroy(): void { override destroy(): void {
this.#inherit = false; this.#inherit = false;
this.#active = false; this.#active.setValue(false);
this.#autoActivate = false; this.#autoActivate = false;
(this as any).provideAt = undefined; (this as any).provideAt = undefined;
this.unprovide(); this.unprovide();