Shortcuts: Implement context and a few shortcuts of interest (#20204)

* setup files

* allow Unproviding as a valid word

* setup context

* declare new module

* clean up on destroy

* implement keydown listener

* rename to all

* Revert "rename to all"

This reverts commit 5384408d5f70111b63a5e07b9b20d6536c530c00.

* revert shortcuts revert

* move view initialization to submittable workspace base

* comment on destroy thingy

* submit workspace shortcut

* rename to action

* observe parent activation to make sure children follows along.

* fix comment to make AI happy

* implement modal view and titles

* fix getting title from token

* rename context alias

* use controller not context here

* provide modal view at modal element

* implement view context at app level

* Refactor view inheritance logic

* reverse children to be activated loop

* note on global shortcuts

* additional note
This commit is contained in:
Niels Lyngsø
2025-09-22 17:19:28 +02:00
committed by GitHub
parent e29e612e46
commit fb0f719c7d
31 changed files with 517 additions and 199 deletions

View File

@@ -1,5 +1,8 @@
{
"cSpell.words": ["unprovide"],
"cSpell.words": [
"unprovide",
"Unproviding"
],
"eslint.useFlatConfig": true,
"eslint.workingDirectories": [
"./src/Umbraco.Web.UI.Client/",

View File

@@ -31,9 +31,9 @@
"./collection": "./dist-cms/packages/core/collection/index.js",
"./components": "./dist-cms/packages/core/components/index.js",
"./const": "./dist-cms/packages/core/const/index.js",
"./content-picker": "./dist-cms/packages/property-editors/content-picker/index.js",
"./content-type": "./dist-cms/packages/content/content-type/index.js",
"./content": "./dist-cms/packages/content/content/index.js",
"./content-picker": "./dist-cms/packages/property-editors/content-picker/index.js",
"./culture": "./dist-cms/packages/core/culture/index.js",
"./current-user": "./dist-cms/packages/user/current-user/index.js",
"./dashboard": "./dist-cms/packages/core/dashboard/index.js",
@@ -47,8 +47,8 @@
"./entity-action": "./dist-cms/packages/core/entity-action/index.js",
"./entity-bulk-action": "./dist-cms/packages/core/entity-bulk-action/index.js",
"./entity-create-option-action": "./dist-cms/packages/core/entity-create-option-action/index.js",
"./entity": "./dist-cms/packages/core/entity/index.js",
"./entity-item": "./dist-cms/packages/core/entity-item/index.js",
"./entity": "./dist-cms/packages/core/entity/index.js",
"./event": "./dist-cms/packages/core/event/index.js",
"./extension-registry": "./dist-cms/packages/core/extension-registry/index.js",
"./health-check": "./dist-cms/packages/health-check/index.js",
@@ -68,9 +68,9 @@
"./media-type": "./dist-cms/packages/media/media-types/index.js",
"./media": "./dist-cms/packages/media/media/index.js",
"./member-group": "./dist-cms/packages/members/member-group/index.js",
"./member-public-access": "./dist-cms/packages/members/member-public-access/index.js",
"./member-type": "./dist-cms/packages/members/member-type/index.js",
"./member": "./dist-cms/packages/members/member/index.js",
"./member-public-access": "./dist-cms/packages/members/member-public-access/index.js",
"./menu": "./dist-cms/packages/core/menu/index.js",
"./modal": "./dist-cms/packages/core/modal/index.js",
"./models": "./dist-cms/packages/core/models/index.js",
@@ -96,9 +96,10 @@
"./search": "./dist-cms/packages/search/index.js",
"./section": "./dist-cms/packages/core/section/index.js",
"./segment": "./dist-cms/packages/segment/index.js",
"./server": "./dist-cms/packages/core/server/index.js",
"./server-file-system": "./dist-cms/packages/core/server-file-system/index.js",
"./server": "./dist-cms/packages/core/server/index.js",
"./settings": "./dist-cms/packages/settings/index.js",
"./shortcut": "./dist-cms/packages/core/shortcut/index.js",
"./sorter": "./dist-cms/packages/core/sorter/index.js",
"./static-file": "./dist-cms/packages/static-file/index.js",
"./store": "./dist-cms/packages/core/store/index.js",

View File

@@ -22,6 +22,7 @@ import { filter, first, firstValueFrom } from '@umbraco-cms/backoffice/external/
import { hasOwnOpener, redirectToStoredPath } from '@umbraco-cms/backoffice/utils';
import { UmbApiInterceptorController } from '@umbraco-cms/backoffice/resources';
import { umbHttpClient } from '@umbraco-cms/backoffice/http-client';
import { UmbViewContext } from '@umbraco-cms/backoffice/view';
import './app-logo.element.js';
import './app-oauth.element.js';
@@ -159,6 +160,8 @@ export class UmbAppElement extends UmbLitElement {
new UmbContextDebugController(this);
new UmbNetworkConnectionStatusManager(this);
new UmbViewContext(this, null);
}
override connectedCallback(): void {

View File

@@ -7,7 +7,6 @@ import {
UmbRequestReloadChildrenOfEntityEvent,
UmbRequestReloadStructureForEntityEvent,
} from '@umbraco-cms/backoffice/entity-action';
import { UmbViewContext } from '@umbraco-cms/backoffice/view';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import type { Observable } from '@umbraco-cms/backoffice/observable-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
@@ -53,8 +52,6 @@ export abstract class UmbContentTypeWorkspaceContextBase<
public readonly structure: UmbContentTypeStructureManager<DetailModelType>;
public readonly view = new UmbViewContext(this, null);
constructor(host: UmbControllerHost, args: UmbContentTypeWorkspaceContextArgs) {
super(host, args);

View File

@@ -33,7 +33,6 @@ import {
} from '@umbraco-cms/backoffice/property';
import { UmbSegmentCollectionRepository } from '@umbraco-cms/backoffice/segment';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
import { UmbViewContext } from '@umbraco-cms/backoffice/view';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import {
UMB_VALIDATION_CONTEXT,
@@ -145,9 +144,6 @@ export abstract class UmbContentDetailWorkspaceContextBase<
readonly collection: UmbContentCollectionManager;
/* View */
readonly view = new UmbViewContext(this, null);
/* Variant Options */
// TODO: Optimize this so it uses either a App Language Context? [NL]
#languageRepository = new UmbLanguageCollectionRepository(this);

View File

@@ -7,7 +7,7 @@ import {
} from '@umbraco-cms/backoffice/content-type';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UMB_VIEW_CONTEXT, UmbViewContext } from '@umbraco-cms/backoffice/view';
import { UMB_VIEW_CONTEXT, UmbViewController } from '@umbraco-cms/backoffice/view';
import type {
PageComponent,
UmbRoute,
@@ -31,7 +31,7 @@ export class UmbContentWorkspaceViewEditElement extends UmbLitElement implements
@state()
private _hasRootProperties = false;
*/
#viewContext?: UmbViewContext;
#viewContext?: typeof UMB_VIEW_CONTEXT.TYPE;
@state()
private _hasRootGroups = false;
@@ -51,7 +51,7 @@ export class UmbContentWorkspaceViewEditElement extends UmbLitElement implements
@state()
private _hintMap: Map<string | null, UmbVariantHint> = new Map();
#tabViewContexts: Array<UmbViewContext> = [];
#tabViewContexts: Array<UmbViewController> = [];
#structureManager?: UmbContentTypeStructureManager<UmbContentTypeModel>;
@@ -150,7 +150,7 @@ export class UmbContentWorkspaceViewEditElement extends UmbLitElement implements
#createViewContext(viewAlias: string | null, tabName: string) {
if (!this.#tabViewContexts.find((context) => context.viewAlias === viewAlias)) {
const view = new UmbViewContext(this, viewAlias);
const view = new UmbViewController(this, viewAlias);
this.#tabViewContexts.push(view);
if (viewAlias === null) {
@@ -176,7 +176,7 @@ export class UmbContentWorkspaceViewEditElement extends UmbLitElement implements
}
}
#currentProvidedView?: UmbViewContext;
#currentProvidedView?: UmbViewController;
#provideViewContext(viewAlias: string | null, component: PageComponent) {
const view = this.#tabViewContexts.find((context) => context.viewAlias === viewAlias);
@@ -188,6 +188,11 @@ export class UmbContentWorkspaceViewEditElement extends UmbLitElement implements
throw new Error(`View context with alias ${viewAlias} not found`);
}
this.#currentProvidedView = view;
// ViewAlias null is only for the root tab, therefor we can implement this hack.
if (viewAlias === null) {
// Specific hack for the Generic tab to only show its name if there are other tabs.
view.setBrowserTitle(this._tabs && this._tabs?.length > 0 ? '#general_generic' : undefined);
}
view.provideAt(component as any);
}

View File

@@ -6,6 +6,7 @@ import type {
UmbInvariantDatasetWorkspaceContext,
UmbRoutableWorkspaceContext,
ManifestWorkspace,
UmbNamableWorkspaceContext,
} from '@umbraco-cms/backoffice/workspace';
import {
UmbSubmittableWorkspaceContextBase,
@@ -26,7 +27,7 @@ type PropertyTypeDataModel = UmbPropertyTypeScaffoldModel;
export class UmbPropertyTypeWorkspaceContext
extends UmbSubmittableWorkspaceContextBase<PropertyTypeDataModel>
implements UmbInvariantDatasetWorkspaceContext, UmbRoutableWorkspaceContext
implements UmbInvariantDatasetWorkspaceContext, UmbRoutableWorkspaceContext, UmbNamableWorkspaceContext
{
// Just for context token safety:
public readonly IS_PROPERTY_TYPE_WORKSPACE_CONTEXT = true;
@@ -62,11 +63,22 @@ export class UmbPropertyTypeWorkspaceContext
this.validationContext = new UmbValidationContext(this);
this.addValidationContext(this.validationContext);
this.observe(this.unique, (unique) => {
if (unique) {
this.validationContext.setDataPath(UmbDataPathPropertyTypeQuery({ id: unique }));
}
});
this.observe(
this.unique,
(unique) => {
if (unique) {
this.validationContext.setDataPath(UmbDataPathPropertyTypeQuery({ id: unique }));
}
},
null,
);
this.observe(
this.name,
(name) => {
this.view.setBrowserTitle(name);
},
null,
);
this.#init = this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, (context) => {
this.#contentTypeContext = context;

View File

@@ -55,6 +55,7 @@ export class UmbModalElement extends UmbLitElement {
}
this.#modalContext.addEventListener('umb:destroy', this.#onContextDestroy);
this.#modalContext.view.provideAt(this);
this.element = await this.#createContainerElement();
// Makes sure that the modal triggers the reject of the context promise when it is closed by pressing escape.

View File

@@ -2,10 +2,11 @@ import { UmbModalToken } from '../token/modal-token.js';
import type { UmbModalConfig, UmbModalType } from '../types.js';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type { UUIModalElement, UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
import { umbDeepMerge } from '@umbraco-cms/backoffice/utils';
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { umbDeepMerge } from '@umbraco-cms/backoffice/utils';
import { UmbId } from '@umbraco-cms/backoffice/id';
import { UmbObjectState, UmbStringState } from '@umbraco-cms/backoffice/observable-api';
import { UmbViewController } from '@umbraco-cms/backoffice/view';
import { UMB_ROUTE_CONTEXT } from '@umbraco-cms/backoffice/router';
import type { ElementLoaderProperty } from '@umbraco-cms/backoffice/extension-api';
import type { IRouterSlot } from '@umbraco-cms/backoffice/router';
@@ -61,6 +62,8 @@ export class UmbModalContext<
#size = new UmbStringState<UUIModalSidebarSize>('small');
public readonly size = this.#size.asObservable();
public readonly view;
constructor(
host: UmbControllerHost,
modalAlias: string | UmbModalToken<ModalData, ModalValue>,
@@ -71,6 +74,9 @@ export class UmbModalContext<
this.router = args.router ?? null;
this.alias = modalAlias;
this.view = new UmbViewController(this, modalAlias.toString());
let title: string | undefined = undefined;
let size = 'small';
if (this.alias instanceof UmbModalToken) {
@@ -78,8 +84,11 @@ export class UmbModalContext<
size = this.alias.getDefaultModal()?.size ?? size;
this.element = this.alias.getDefaultModal()?.element || this.element;
this.backdropBackground = this.alias.getDefaultModal()?.backdropBackground || this.backdropBackground;
title = this.alias.getDefaultModal()?.title ?? undefined;
}
this.view.setBrowserTitle(title);
this.type = args.modal?.type || this.type;
size = args.modal?.size ?? size;
this.element = args.modal?.element || this.element;

View File

@@ -35,4 +35,9 @@ export interface UmbModalConfig {
* Set the background property of the modal backdrop
*/
backdropBackground?: string;
/**
* Set the title of the modal, this is used as Browser Title
*/
title?: string;
}

View File

@@ -1,4 +1,5 @@
import type { UmbItemRepository } from './item/index.js';
import type { UmbRepositoryItemsStatus } from './types.js';
import { UmbDeprecation } from '@umbraco-cms/backoffice/utils';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
@@ -7,7 +8,6 @@ import { UmbExtensionApiInitializer } from '@umbraco-cms/backoffice/extension-ap
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import { UmbEntityUpdatedEvent } from '@umbraco-cms/backoffice/entity-action';
import type { UmbRepositoryItemsStatus } from './types.js';
const ObserveRepositoryAlias = Symbol();

View File

@@ -0,0 +1,3 @@
export * from './shortcut.context-token.js';
export * from './shortcut.context.js';
export * from './shortcut.controller.js';

View File

@@ -0,0 +1,4 @@
import type { UmbShortcutController } from './shortcut.controller.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
export const UMB_SHORTCUT_CONTEXT = new UmbContextToken<UmbShortcutController>('UmbShortcutContext');

View File

@@ -0,0 +1,10 @@
import { UMB_SHORTCUT_CONTEXT } from './shortcut.context-token.js';
import { UmbShortcutController } from './shortcut.controller.js';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
export class UmbShortcutContext extends UmbShortcutController {
constructor(host: UmbControllerHost) {
super(host);
this.provideContext(UMB_SHORTCUT_CONTEXT, this as unknown as UmbShortcutContext);
}
}

View File

@@ -0,0 +1,189 @@
import type { UmbShortcut } from '../types.js';
import { UMB_SHORTCUT_CONTEXT } from './shortcut.context-token.js';
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerBase, type UmbClassInterface } from '@umbraco-cms/backoffice/class-api';
import type { UmbContextProviderController } from '@umbraco-cms/backoffice/context-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type { UmbPartialSome } from '@umbraco-cms/backoffice/utils';
type IncomingShortcutType = UmbPartialSome<UmbShortcut, 'unique' | 'weight' | 'modifier' | 'shift' | 'alt'>;
const IsMac = navigator.userAgent ? /Mac/i.test(navigator.userAgent) : navigator.platform.toUpperCase().includes('MAC');
export class UmbShortcutController extends UmbControllerBase {
//
#inUnprovidingState = false;
#parent?: UmbShortcutController;
readonly #shortcuts = new UmbArrayState<UmbShortcut>([], (x) => x.unique);
public readonly all = this.#shortcuts.asObservable();
constructor(host: UmbControllerHost) {
super(host);
this.#shortcuts.sortBy((a, b) => (b.weight || 0) - (a.weight || 0));
}
#providerCtrl?: UmbContextProviderController;
#currentProvideHost?: UmbClassInterface;
/**
* Provide this validation context to a specific controller host.
* This can be used to Host a validation context in a Workspace, but provide it on a certain scope, like a specific Workspace View.
* @param {UmbClassInterface} controllerHost - The controller host to provide this validation context to.
*/
provideAt(controllerHost: UmbClassInterface): void {
if (this.#currentProvideHost === controllerHost) return;
this.unprovide();
this.#currentProvideHost = controllerHost;
this.#providerCtrl = controllerHost.provideContext(UMB_SHORTCUT_CONTEXT, this as any);
}
unprovide(): void {
if (this.#providerCtrl) {
// We need to set this in Unprovide state, so this context can be provided again later.
this.#inUnprovidingState = true;
this.#providerCtrl.destroy();
this.#providerCtrl = undefined;
this.#inUnprovidingState = false;
this.#currentProvideHost = undefined;
}
}
inherit(): void {
this.consumeContext(UMB_SHORTCUT_CONTEXT, (parent) => {
this.inheritFrom(parent);
}).skipHost();
// Notice skipHost ^^, this is because we do not want it to consume it self, as this would be a match for this consumption, instead we will look at the parent and above. [NL]
}
inheritFrom(parent: UmbShortcutController | undefined): void {
if (this.#parent === parent) return;
this.#parent = parent;
}
initiateChange() {
this.#shortcuts.mute();
}
finishChange() {
this.#shortcuts.unmute();
}
/**
* Add a new hint
* @param {IncomingShortcutType} shortcut - The hint to add
* @returns {UmbShortcut['unique']} Unique value of the hint
*/
addOne(shortcut: IncomingShortcutType): string | symbol {
const newShortcut = { ...shortcut } as unknown as UmbShortcut;
newShortcut.unique ??= Symbol();
newShortcut.weight ??= 0;
newShortcut.modifier ??= false;
newShortcut.shift ??= false;
newShortcut.alt ??= false;
this.#shortcuts.appendOne(newShortcut);
return shortcut.unique!;
}
/**
* Add multiple rules
* @param {IncomingShortcutType[]} shortcuts - Array of hints to add
*/
add(shortcuts: IncomingShortcutType[]) {
this.#shortcuts.mute();
shortcuts.forEach((hint) => this.addOne(hint));
this.#shortcuts.unmute();
}
/**
* Remove a hint
* @param {UmbShortcut['unique']} unique Unique value of the hint to remove
*/
removeOne(unique: UmbShortcut['unique']) {
this.#shortcuts.removeOne(unique);
}
/**
* Remove multiple hints
* @param {UmbShortcut['unique'][]} uniques Array of unique values to remove
*/
remove(uniques: UmbShortcut['unique'][]) {
this.#shortcuts.remove(uniques);
}
/**
* Check if a hint exists
* @param {UmbShortcut['unique']} unique Unique value of the hint to check
* @returns {boolean} True if the hint exists, false otherwise
*/
has(unique: UmbShortcut['unique']): boolean {
return this.#shortcuts.getHasOne(unique);
}
/**
* Get all hints
* @returns {UmbShortcut[]} Array of hints
*/
getAll(): UmbShortcut[] {
return this.#shortcuts.getValue();
}
/**
* Get all hints
* @param key
* @param modifier
* @param shift
* @param alt
* @returns {UmbShortcut[]} Array of hints
*/
findShortcut(key: string, modifier: boolean, shift: boolean = false, alt: boolean = false): UmbShortcut | undefined {
const shortcuts = this.#shortcuts.getValue();
for (const s of shortcuts) {
if (s.key.toLowerCase() === key.toLowerCase() && s.modifier === modifier && s.shift === shift && s.alt === alt) {
return s;
}
}
return undefined;
}
/**
* Clear all hints
*/
clear(): void {
this.#shortcuts.setValue([]);
}
activate() {
window.addEventListener('keydown', this.#onKeyDown);
}
deactivate() {
window.removeEventListener('keydown', this.#onKeyDown);
}
#onKeyDown = (e: KeyboardEvent) => {
const keyDown = e.key.toLowerCase();
const modifierDown = IsMac ? e.metaKey : e.ctrlKey;
const shortcut = this.findShortcut(keyDown, modifierDown, e.shiftKey, e.altKey);
if (shortcut) {
e.preventDefault();
shortcut.action();
}
};
override destroy(): void {
super.destroy();
if (this.#inUnprovidingState === true) {
// TODO: What is it i'm doing here, check if it actually makes sense, if so add a comment on why [NL]
return;
}
this.unprovide();
this.#parent = undefined;
this.#shortcuts.destroy();
}
}

View File

@@ -0,0 +1,2 @@
export * from './context/index.js';
export type * from './types.js';

View File

@@ -0,0 +1,12 @@
export interface UmbShortcut {
unique: string | symbol;
key: string;
modifier: boolean;
shift: boolean;
alt: boolean;
label?: string;
weight?: number;
action: () => void | Promise<void>;
// TODO: Consider implementing a global option, to make a shortcut be available despite children setting up their own inheritance scopes. [NL]
// TODO: Addition thought, also a bit dangerous cause how do you know the interest of the children. [NL]
}

View File

@@ -1,2 +1,3 @@
export * from './view.controller.js';
export * from './view.context.js';
export * from './view.context-token.js';

View File

@@ -1,10 +1,6 @@
import { UmbShortcutController } from '../../shortcut/context/shortcut.controller.js';
import { UMB_VIEW_CONTEXT } from './view.context-token.js';
import {
UmbBooleanState,
UmbClassState,
UmbStringState,
mergeObservables,
} from '@umbraco-cms/backoffice/observable-api';
import { UmbClassState, UmbStringState, mergeObservables } from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { UmbHintController } from '@umbraco-cms/backoffice/hint';
import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api';
@@ -14,12 +10,7 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type { UmbVariantHint } from '@umbraco-cms/backoffice/hint';
import type { UmbVariantId } from '@umbraco-cms/backoffice/variant';
const ObserveParentActiveCtrlAlias = Symbol();
/**
*
* TODO:
* Include Shortcuts
*
* The View Context handles the aspects of three Features:
* Browser Titles — Provide a title for this view and it will be set or joint with parent views depending on the inheritance setting.
@@ -28,6 +19,8 @@ const ObserveParentActiveCtrlAlias = Symbol();
*
*/
export class UmbViewController extends UmbControllerBase {
//
static #ActiveView?: UmbViewController;
//
#attached = false;
#providerCtrl?: UmbContextProviderController;
@@ -37,13 +30,34 @@ export class UmbViewController extends UmbControllerBase {
// State used to know if the context can be auto activated when attached.
#autoActivate = true;
#active = new UmbBooleanState(false);
public readonly active = this.#active.asObservable();
#active = false;
get isActive() {
return this.#active.getValue();
return this.#active;
}
#hasActiveChild = false;
#inherit?: boolean;
#setActive() {
this.#active = true;
if (this.#inherit) {
// Secure the parent in the inheritance chain is active.
this.#parentView?._internal_activate();
} else {
// This is for a single, or top level of the inheritance chain, so we can disable the previous active view.
if (UmbViewController.#ActiveView && UmbViewController.#ActiveView !== this) {
UmbViewController.#ActiveView._internal_deactivate();
UmbViewController.#ActiveView = undefined;
}
UmbViewController.#ActiveView = this;
}
}
#removeActive() {
this.#active = false;
if (!this.#inherit) {
if (UmbViewController.#ActiveView === this) {
UmbViewController.#ActiveView = undefined;
}
}
}
#inherit = false;
#explicitInheritance?: boolean;
#parentView?: UmbViewController;
#title?: string;
@@ -55,9 +69,11 @@ export class UmbViewController extends UmbControllerBase {
#variantId = new UmbClassState<UmbVariantId | undefined>(undefined);
protected readonly variantId = this.#variantId.asObservable();
public hints;
public readonly hints;
readonly firstHintOfVariant;
public readonly shortcuts = new UmbShortcutController(this);
public readonly firstHintOfVariant;
constructor(host: UmbControllerHost, viewAlias: string | null) {
super(host);
@@ -79,24 +95,25 @@ export class UmbViewController extends UmbControllerBase {
this.#consumeParentCtrl = this.consumeContext(UMB_VIEW_CONTEXT, (parentView) => {
// In case of explicit inheritance we do not want to overview the parent view.
if (this.#explicitInheritance) return;
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]
this.#propagateActivation();
}
this.#active.setValue(false);
if (parentView) {
this.#parentView = parentView;
}
if (this.#inherit) {
this.#inheritFromParent();
this.#setParentView(parentView);
}
// only activate if we had an incoming parentView, cause if not we are in a disassembling state. [NL]
if (parentView && this.#attached && this.#autoActivate) {
this._internal_activate();
this._internal_requestActivate();
}
}).skipHost();
}
#setParentView(view: UmbViewController | undefined) {
if (this.#parentView === view) return;
this.#parentView = view;
if (this.#inherit) {
this.#inheritFromParent();
}
}
public setVariantId(variantId: UmbVariantId | undefined): void {
this.#variantId.setValue(variantId);
this.hints.updateScaffold({ variantId: variantId });
@@ -105,7 +122,6 @@ export class UmbViewController extends UmbControllerBase {
public setBrowserTitle(title: string | undefined): void {
if (this.#title === title) return;
this.#title = title;
// TODO: This check should be if its the most child being active, but again think about how the parents in the active chain should work.
this.#computeTitle();
this.#updateTitle();
}
@@ -119,9 +135,10 @@ export class UmbViewController extends UmbControllerBase {
this.#currentProvideHost = controllerHost;
this.#providerCtrl = controllerHost.provideContext(UMB_VIEW_CONTEXT, this);
this.hints.provideAt(controllerHost);
this.shortcuts.provideAt(controllerHost);
if (this.#attached && this.#autoActivate) {
this._internal_activate();
if (this.#attached) {
this._internal_requestActivate();
}
}
@@ -131,30 +148,41 @@ export class UmbViewController extends UmbControllerBase {
this.#providerCtrl = undefined;
}
this.hints.unprovide();
this.shortcuts.unprovide();
this._internal_deactivate();
this.#requestActivateParent();
}
override hostConnected(): void {
const wasActive = this.isActive;
const wasAttached = this.#attached;
this.#attached = true;
super.hostConnected();
if (!wasAttached) {
this.#parentView?._internal_addChild(this);
}
// Check that we have a providerController, otherwise this is not provided. [NL]
if (this.#autoActivate && !wasActive) {
this._internal_activate();
this._internal_requestActivate();
}
}
override hostDisconnected(): void {
const wasAttached = this.#attached;
const wasActive = this.isActive;
this.#attached = false;
this.#active.setValue(false);
super.hostDisconnected();
if (wasAttached === true && wasActive) {
// Check that we have a providerController, otherwise this is not provided. [NL]
this.#propagateActivation();
if (wasAttached) {
this.#parentView?._internal_removeChild(this);
}
this._internal_deactivate();
super.hostDisconnected();
this.#autoActivate = true;
this.#requestActivateParent();
}
public isInheriting() {
return this.#inherit;
}
public inherit() {
@@ -166,21 +194,7 @@ export class UmbViewController extends UmbControllerBase {
this.#explicitInheritance = true;
this.#consumeParentCtrl?.destroy();
this.#consumeParentCtrl = undefined;
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.#propagateActivation();
this.#setParentView(context);
}
#inheritFromParent(): void {
@@ -205,19 +219,10 @@ export class UmbViewController extends UmbControllerBase {
this.hints.inheritFrom(this.#parentView?.hints);
}
#propagateActivation() {
if (!this.#parentView) return;
if (this.#inherit) {
if (this.isActive) {
this.#parentView._internal_childActivated();
} else {
this.#parentView._internal_childDeactivated();
}
} else {
if (this.isActive) {
this.#parentView._internal_deactivate();
} else {
this.#parentView._internal_activate();
#requestActivateParent() {
if (!this.#inherit) {
if (this.#parentView) {
this.#parentView._internal_requestActivate();
}
}
}
@@ -227,58 +232,52 @@ export class UmbViewController extends UmbControllerBase {
* Notify that a view context has been activated.
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
public _internal_activate() {
public _internal_requestActivate(): boolean {
if (!this.#providerCtrl) {
// If we are not provided we should not be activated. [NL]
return;
return false;
}
// TODO: Check this one: We do not want a parent to auto activate if a child is having the activation. [NL], well maybe it not that bad because of the asking of the children...
this.#autoActivate = true;
if (this.isActive) {
return;
return true;
}
// If not attached then propagate the activation to the parent. [NL]
if (this.#attached === false) {
if (!this.#parentView) {
throw new Error('Cannot activate a view that is not attached to the DOM.');
}
this.#propagateActivation();
} else {
this.#active.setValue(true);
this.#propagateActivation();
this.#updateTitle();
// TODO: Start shortcuts. [NL]
}
}
/**
* @internal
* Notify that a child has been activated.
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
public _internal_childActivated() {
if (this.#hasActiveChild) return;
this.#hasActiveChild = true;
this._internal_activate();
}
/**
* @internal
* Notify that a child is no longer activated.
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
public _internal_childDeactivated() {
this.#hasActiveChild = false;
if (this.#attached === false) {
if (this.#parentView) {
return;
} else {
throw new Error('Cannot re-activate(_childDeactivated) a view that is not attached to the DOM.');
// Check if any of the children likes to be activated instead:
// A reverse loop ensures latest added child gets first chance to activate. This may matter in some future issue-scenario, I will say it could be that it is not the right way to determine if multiple children wants to be active. [NL]
let i = this.#children.length;
while (i--) {
const child = this.#children[i];
if (child._internal_requestActivate()) {
// If we have an active child we should not update the title.
return true;
}
}
// if not then check your self:
if (this.#autoActivate && this.#attached) {
this._internal_activate();
return true;
}
}
if (this.#autoActivate) {
this._internal_activate();
} else {
this.#propagateActivation();
return false;
}
/**
* @internal
* Notify that a view context has been activated.
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
public _internal_activate() {
if (this.#attached) {
this.#autoActivate = true;
this.#setActive();
this.#updateTitle();
this.shortcuts.activate();
}
}
@@ -289,16 +288,21 @@ export class UmbViewController extends UmbControllerBase {
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
public _internal_deactivate() {
this.#autoActivate = false;
if (!this.isActive) return;
this.#active.setValue(false);
// TODO: Stop shortcuts. [NL]
// Deactivate parents:
this.#propagateActivation();
this.#autoActivate = false;
// Deactive children:
this.#children.forEach((child) => {
if (child.isInheriting()) {
child._internal_deactivate();
}
});
this.shortcuts.deactivate();
this.#removeActive();
}
#updateTitle() {
if (!this.#active || this.#hasActiveChild) {
if (!this.#active || this.#hasActiveChildren()) {
return;
}
const localTitle = this.getComputedTitle();
@@ -320,9 +324,32 @@ export class UmbViewController extends UmbControllerBase {
return this.#computedTitle.getValue();
}
#children: UmbViewController[] = [];
// eslint-disable-next-line @typescript-eslint/naming-convention
public _internal_addChild(child: UmbViewController) {
this.#children.push(child);
if (this.isActive) {
child._internal_activate();
}
}
// eslint-disable-next-line @typescript-eslint/naming-convention
public _internal_removeChild(child: UmbViewController) {
const index = this.#children.indexOf(child);
if (index !== -1) {
this.#children.splice(index, 1);
}
// update title?
if (this.#active && !this.#hasActiveChildren()) {
this.#updateTitle();
}
}
#hasActiveChildren() {
return this.#children.some((child) => child.isActive);
}
override destroy(): void {
this.#inherit = false;
this.#active.setValue(false);
this.#removeActive();
this.#autoActivate = false;
(this as any).provideAt = undefined;
this.unprovide();

View File

@@ -26,18 +26,18 @@ export default defineConfig({
'entity-action/index': './entity-action/index.ts',
'entity-bulk-action/index': './entity-bulk-action/index.ts',
'entity-create-option-action/index': './entity-create-option-action/index.ts',
'entity/index': './entity/index.ts',
'entity-item/index': './entity-item/index.ts',
'entity/index': './entity/index.ts',
'entry-point': 'entry-point.ts',
'event/index': './event/index.ts',
'extension-registry/index': './extension-registry/index.ts',
'http-client/index': './http-client/index.ts',
'hint/index': './hint/index.ts',
'http-client/index': './http-client/index.ts',
'icon-registry/index': './icon-registry/index.ts',
'id/index': './id/index.ts',
'interaction-memory/index': './interaction-memory/index.ts',
'lit-element/index': './lit-element/index.ts',
'localization/index': './localization/index.ts',
'interaction-memory/index': './interaction-memory/index.ts',
'menu/index': './menu/index.ts',
'modal/index': './modal/index.ts',
'models/index': './models/index.ts',
@@ -53,8 +53,9 @@ export default defineConfig({
'resources/index': './resources/index.ts',
'router/index': './router/index.ts',
'section/index': './section/index.ts',
'server/index': './server/index.ts',
'server-file-system/index': './server-file-system/index.ts',
'server/index': './server/index.ts',
'shortcut/index': './shortcut/index.ts',
'sorter/index': './sorter/index.ts',
'store/index': './store/index.ts',
'style/index': './style/index.ts',

View File

@@ -2,7 +2,6 @@ import type { UmbNamableWorkspaceContext } from '../types.js';
import { UmbNameWriteGuardManager } from '../namable/index.js';
import { UmbEntityDetailWorkspaceContextBase } from './entity-detail-workspace-base.js';
import type { UmbEntityDetailWorkspaceContextArgs, UmbEntityDetailWorkspaceContextCreateArgs } from './types.js';
import { UmbViewContext } from '@umbraco-cms/backoffice/view';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type { UmbDetailRepository } from '@umbraco-cms/backoffice/repository';
import type { UmbNamedEntityModel } from '@umbraco-cms/backoffice/entity';
@@ -24,8 +23,6 @@ export abstract class UmbEntityNamedDetailWorkspaceContextBase<
public readonly nameWriteGuard = new UmbNameWriteGuardManager(this);
public readonly view = new UmbViewContext(this, null);
constructor(host: UmbControllerHost, args: UmbEntityDetailWorkspaceContextArgs) {
super(host, args);
this.nameWriteGuard.fallbackToPermitted();

View File

@@ -1,22 +1,26 @@
import { UMB_WORKSPACE_CONTEXT } from '../../workspace.context-token.js';
import type { UmbWorkspaceContext } from '../../workspace-context.interface.js';
import type { ManifestWorkspace } from '../../extensions/types.js';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import { UmbEntityContext, type UmbEntityUnique } from '@umbraco-cms/backoffice/entity';
import { UmbViewContext } from '@umbraco-cms/backoffice/view';
import type { ManifestWorkspaceDefaultKind } from './types.js';
export class UmbDefaultWorkspaceContext extends UmbContextBase implements UmbWorkspaceContext {
public workspaceAlias!: string;
#entityContext = new UmbEntityContext(this);
public readonly view = new UmbViewContext(this, null);
constructor(host: UmbControllerHost) {
super(host, UMB_WORKSPACE_CONTEXT.toString());
}
set manifest(manifest: ManifestWorkspace) {
set manifest(manifest: ManifestWorkspaceDefaultKind) {
this.workspaceAlias = manifest.alias;
this.setEntityType(manifest.meta.entityType);
this.view.setBrowserTitle(manifest.meta.headline);
}
setUnique(unique: UmbEntityUnique): void {

View File

@@ -8,6 +8,7 @@ import type { UmbModalContext } from '@umbraco-cms/backoffice/modal';
import { UMB_MODAL_CONTEXT } from '@umbraco-cms/backoffice/modal';
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
import type { UmbValidationController } from '@umbraco-cms/backoffice/validation';
import { UmbViewContext } from '@umbraco-cms/backoffice/view';
export abstract class UmbSubmittableWorkspaceContextBase<WorkspaceDataModelType>
extends UmbContextBase
@@ -20,6 +21,8 @@ export abstract class UmbSubmittableWorkspaceContextBase<WorkspaceDataModelType>
#validationContexts: Array<UmbValidationController> = [];
public readonly view = new UmbViewContext(this, null);
/**
* Appends a validation context to the workspace.
* @param context
@@ -54,6 +57,13 @@ export abstract class UmbSubmittableWorkspaceContextBase<WorkspaceDataModelType>
this.consumeContext(UMB_MODAL_CONTEXT, (context) => {
(this.modalContext as UmbModalContext | undefined) = context;
});
this.view.shortcuts.addOne({
key: 's',
modifier: true,
action: () => this.requestSubmit(),
label: '#general_submit',
});
}
protected resetState() {

View File

@@ -1,9 +1,10 @@
import type { UmbDocumentVariantOptionModel } from '../../types.js';
import type { UmbDocumentSaveModalData, UmbDocumentSaveModalValue } from './document-save-modal.token.js';
import { css, customElement, html, state } from '@umbraco-cms/backoffice/external/lit';
import { umbFocus } from '@umbraco-cms/backoffice/lit-element';
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import '../shared/document-variant-language-picker.element.js';
@@ -56,21 +57,23 @@ export class UmbDocumentSaveModalElement extends UmbModalBaseElement<
}
override render() {
return html`<uui-dialog-layout headline=${this.localize.term('content_saveModalTitle')}>
<umb-document-variant-language-picker
.selectionManager=${this.#selectionManager}
.variantLanguageOptions=${this._options}
.pickableFilter=${this.#pickableFilter}></umb-document-variant-language-picker>
<div slot="actions">
<uui-button label=${this.localize.term('general_close')} @click=${this.#close}></uui-button>
<uui-button
label="${this.localize.term('buttons_save')}"
look="primary"
color="positive"
@click=${this.#submit}></uui-button>
</div>
</uui-dialog-layout> `;
return html`
<uui-dialog-layout headline=${this.localize.term('content_saveModalTitle')}>
<umb-document-variant-language-picker
.selectionManager=${this.#selectionManager}
.variantLanguageOptions=${this._options}
.pickableFilter=${this.#pickableFilter}></umb-document-variant-language-picker>
<div slot="actions">
<uui-button label=${this.localize.term('general_close')} @click=${this.#close}></uui-button>
<uui-button
${umbFocus()}
label="${this.localize.term('buttons_save')}"
look="primary"
color="positive"
@click=${this.#submit}></uui-button>
</div>
</uui-dialog-layout>
`;
}
static override styles = [

View File

@@ -2,9 +2,10 @@ import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../
import { isNotPublishedMandatory } from '../../utils.js';
import type { UmbDocumentPublishModalData, UmbDocumentPublishModalValue } from './document-publish-modal.token.js';
import { css, customElement, html, state, when } from '@umbraco-cms/backoffice/external/lit';
import { umbFocus } from '@umbraco-cms/backoffice/lit-element';
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import '../../../modals/shared/document-variant-language-picker.element.js';
@@ -103,33 +104,34 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement<
override render() {
const headline = this.data?.headline ?? this.localize.term('content_publishModalTitle');
return html`<uui-dialog-layout headline=${headline}>
<p>
<umb-localize key="prompt_confirmPublish"></umb-localize>
</p>
return html`
<uui-dialog-layout headline=${headline}>
<p><umb-localize key="prompt_confirmPublish"></umb-localize></p>
${when(
!this._isInvariant,
() =>
html` <umb-document-variant-language-picker
.selectionManager=${this.#selectionManager}
.variantLanguageOptions=${this._options}
.requiredFilter=${isNotPublishedMandatory}
.pickableFilter=${this.#pickableFilter}></umb-document-variant-language-picker>`,
)}
${when(
!this._isInvariant,
() =>
html`<umb-document-variant-language-picker
.selectionManager=${this.#selectionManager}
.variantLanguageOptions=${this._options}
.requiredFilter=${isNotPublishedMandatory}
.pickableFilter=${this.#pickableFilter}></umb-document-variant-language-picker>`,
)}
<div slot="actions">
<uui-button label=${this.localize.term('general_close')} @click=${this.#close}></uui-button>
<uui-button
label="${this.data?.confirmLabel
? this.localize.string(this.data.confirmLabel)
: this.localize.term('buttons_saveAndPublish')}"
look="primary"
color="positive"
?disabled=${this._hasNotSelectedMandatory}
@click=${this.#submit}></uui-button>
</div>
</uui-dialog-layout>`;
<div slot="actions">
<uui-button label=${this.localize.term('general_close')} @click=${this.#close}></uui-button>
<uui-button
${umbFocus()}
label="${this.data?.confirmLabel
? this.localize.string(this.data.confirmLabel)
: this.localize.term('buttons_saveAndPublish')}"
look="primary"
color="positive"
?disabled=${this._hasNotSelectedMandatory}
@click=${this.#submit}></uui-button>
</div>
</uui-dialog-layout>
`;
}
static override styles = [

View File

@@ -1 +1,3 @@
export * from './document-publishing.workspace-context.token.js';
export const UMB_DOCUMENT_PUBLISHING_SHORTCUT_UNIQUE = 'umb-document-publishing-shortcut';

View File

@@ -11,21 +11,22 @@ import { UMB_DOCUMENT_PUBLISH_WITH_DESCENDANTS_MODAL } from '../publish-with-des
import { UMB_DOCUMENT_PUBLISH_MODAL } from '../publish/constants.js';
import { UmbUnpublishDocumentEntityAction } from '../unpublish/index.js';
import { UMB_DOCUMENT_PUBLISHING_WORKSPACE_CONTEXT } from './document-publishing.workspace-context.token.js';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import { UMB_DOCUMENT_PUBLISHING_SHORTCUT_UNIQUE } from './constants.js';
import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
import { observeMultiple } from '@umbraco-cms/backoffice/observable-api';
import { umbOpenModal } from '@umbraco-cms/backoffice/modal';
import { DocumentVariantStateModel } from '@umbraco-cms/backoffice/external/backend-api';
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api';
import {
UmbRequestReloadChildrenOfEntityEvent,
UmbRequestReloadStructureForEntityEvent,
} from '@umbraco-cms/backoffice/entity-action';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification';
import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
import { observeMultiple } from '@umbraco-cms/backoffice/observable-api';
import { DocumentVariantStateModel } from '@umbraco-cms/backoffice/external/backend-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type { UmbEntityUnique } from '@umbraco-cms/backoffice/entity';
import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api';
export class UmbDocumentPublishingWorkspaceContext extends UmbContextBase {
/**
@@ -48,7 +49,18 @@ export class UmbDocumentPublishingWorkspaceContext extends UmbContextBase {
this.#init = Promise.all([
this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, async (context) => {
if (this.#documentWorkspaceContext) {
// remove shortcut:
this.#documentWorkspaceContext.view.shortcuts.removeOne(UMB_DOCUMENT_PUBLISHING_SHORTCUT_UNIQUE);
}
this.#documentWorkspaceContext = context;
this.#documentWorkspaceContext?.view.shortcuts.addOne({
unique: UMB_DOCUMENT_PUBLISHING_SHORTCUT_UNIQUE,
label: this.#localize.term('content_saveAndPublishShortcut'),
key: 'p',
modifier: true,
action: () => this.saveAndPublish(),
});
this.#initPendingChanges();
})
.asPromise({ preventTimeout: true })

View File

@@ -15,6 +15,7 @@ import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import { query } from '@umbraco-cms/backoffice/router';
import type { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
import { UmbViewContext } from '@umbraco-cms/backoffice/view';
export type UmbPoolingInterval = 0 | 2000 | 5000 | 10000 | 20000 | 30000;
export interface UmbPoolingConfig {
@@ -31,6 +32,8 @@ export class UmbLogViewerWorkspaceContext extends UmbContextBase implements UmbW
public readonly workspaceAlias: string = 'Umb.Workspace.LogViewer';
#repository: UmbLogViewerRepository;
public readonly view = new UmbViewContext(this, null);
getEntityType() {
return 'log-viewer';
}
@@ -108,6 +111,8 @@ export class UmbLogViewerWorkspaceContext extends UmbContextBase implements UmbW
// TODO: Revisit usage of workspace for this case... currently no other workspace context provides them self with their own token, we need to update UMB_APP_LOG_VIEWER_CONTEXT to become a workspace context. [NL]
this.provideContext(UMB_WORKSPACE_CONTEXT, this);
this.#repository = new UmbLogViewerRepository(host);
this.view.setBrowserTitle('#treeHeaders_logViewer');
}
override hostConnected() {

View File

@@ -4,5 +4,6 @@ export const UMB_CURRENT_USER_MODAL = new UmbModalToken('Umb.Modal.CurrentUser',
modal: {
type: 'sidebar',
size: 'small',
title: '#general_user',
},
});

View File

@@ -6,7 +6,7 @@ export const manifests: Array<UmbExtensionManifest> = [
type: 'workspace',
kind: 'routable',
alias: UMB_WEBHOOK_WORKSPACE_ALIAS,
name: 'Webhook Root Workspace',
name: 'Webhook Workspace',
api: () => import('./webhook-workspace.context.js'),
meta: {
entityType: UMB_WEBHOOK_ENTITY_TYPE,

View File

@@ -58,9 +58,9 @@ DON'T EDIT THIS FILE DIRECTLY. It is generated by /devops/tsconfig/index.js
"@umbraco-cms/backoffice/collection": ["./src/packages/core/collection/index.ts"],
"@umbraco-cms/backoffice/components": ["./src/packages/core/components/index.ts"],
"@umbraco-cms/backoffice/const": ["./src/packages/core/const/index.ts"],
"@umbraco-cms/backoffice/content-picker": ["./src/packages/property-editors/content-picker/index.ts"],
"@umbraco-cms/backoffice/content-type": ["./src/packages/content/content-type/index.ts"],
"@umbraco-cms/backoffice/content": ["./src/packages/content/content/index.ts"],
"@umbraco-cms/backoffice/content-picker": ["./src/packages/property-editors/content-picker/index.ts"],
"@umbraco-cms/backoffice/culture": ["./src/packages/core/culture/index.ts"],
"@umbraco-cms/backoffice/current-user": ["./src/packages/user/current-user/index.ts"],
"@umbraco-cms/backoffice/dashboard": ["./src/packages/core/dashboard/index.ts"],
@@ -76,8 +76,8 @@ DON'T EDIT THIS FILE DIRECTLY. It is generated by /devops/tsconfig/index.js
"@umbraco-cms/backoffice/entity-create-option-action": [
"./src/packages/core/entity-create-option-action/index.ts"
],
"@umbraco-cms/backoffice/entity": ["./src/packages/core/entity/index.ts"],
"@umbraco-cms/backoffice/entity-item": ["./src/packages/core/entity-item/index.ts"],
"@umbraco-cms/backoffice/entity": ["./src/packages/core/entity/index.ts"],
"@umbraco-cms/backoffice/event": ["./src/packages/core/event/index.ts"],
"@umbraco-cms/backoffice/extension-registry": ["./src/packages/core/extension-registry/index.ts"],
"@umbraco-cms/backoffice/health-check": ["./src/packages/health-check/index.ts"],
@@ -97,9 +97,9 @@ DON'T EDIT THIS FILE DIRECTLY. It is generated by /devops/tsconfig/index.js
"@umbraco-cms/backoffice/media-type": ["./src/packages/media/media-types/index.ts"],
"@umbraco-cms/backoffice/media": ["./src/packages/media/media/index.ts"],
"@umbraco-cms/backoffice/member-group": ["./src/packages/members/member-group/index.ts"],
"@umbraco-cms/backoffice/member-public-access": ["./src/packages/members/member-public-access/index.ts"],
"@umbraco-cms/backoffice/member-type": ["./src/packages/members/member-type/index.ts"],
"@umbraco-cms/backoffice/member": ["./src/packages/members/member/index.ts"],
"@umbraco-cms/backoffice/member-public-access": ["./src/packages/members/member-public-access/index.ts"],
"@umbraco-cms/backoffice/menu": ["./src/packages/core/menu/index.ts"],
"@umbraco-cms/backoffice/modal": ["./src/packages/core/modal/index.ts"],
"@umbraco-cms/backoffice/models": ["./src/packages/core/models/index.ts"],
@@ -125,9 +125,10 @@ DON'T EDIT THIS FILE DIRECTLY. It is generated by /devops/tsconfig/index.js
"@umbraco-cms/backoffice/search": ["./src/packages/search/index.ts"],
"@umbraco-cms/backoffice/section": ["./src/packages/core/section/index.ts"],
"@umbraco-cms/backoffice/segment": ["./src/packages/segment/index.ts"],
"@umbraco-cms/backoffice/server": ["./src/packages/core/server/index.ts"],
"@umbraco-cms/backoffice/server-file-system": ["./src/packages/core/server-file-system/index.ts"],
"@umbraco-cms/backoffice/server": ["./src/packages/core/server/index.ts"],
"@umbraco-cms/backoffice/settings": ["./src/packages/settings/index.ts"],
"@umbraco-cms/backoffice/shortcut": ["./src/packages/core/shortcut/index.ts"],
"@umbraco-cms/backoffice/sorter": ["./src/packages/core/sorter/index.ts"],
"@umbraco-cms/backoffice/static-file": ["./src/packages/static-file/index.ts"],
"@umbraco-cms/backoffice/store": ["./src/packages/core/store/index.ts"],