Merge branch 'main' into feature/store-extension-point

This commit is contained in:
Mads Rasmussen
2023-03-02 20:36:03 +01:00
74 changed files with 14445 additions and 265 deletions

View File

@@ -2,7 +2,6 @@
"ignorePatterns": ["vite.*.ts"],
"root": true,
"plugins": ["eslint-plugin-local-rules"],
"extends": ["eslint:recommended", "plugin:import/recommended", "prettier"],
"overrides": [
{
"files": ["**/*.ts"],
@@ -49,6 +48,27 @@
}
}
}
},
{
"files": ["**/*.js"],
"extends": ["eslint:recommended", "plugin:import/recommended", "prettier"],
"env": {
"node": true,
"browser": true,
"es6": true
},
"parserOptions": {
"sourceType": "module",
"ecmaVersion": "latest"
},
"settings": {
"import/resolver": {
"node": {
"extensions": [".js"],
"moduleDirectory": ["node_modules"]
}
}
}
}
]
}

View File

@@ -11,6 +11,7 @@ node_modules
dist
dist-ssr
types
*.tsbuildinfo
*.local
*.tgz

View File

@@ -2,127 +2,122 @@
/* tslint:disable */
/* eslint-disable */
export class CancelError extends Error {
constructor(message: string) {
super(message);
this.name = 'CancelError';
}
constructor(message: string) {
super(message);
this.name = 'CancelError';
}
public get isCancelled(): boolean {
return true;
}
public get isCancelled(): boolean {
return true;
}
}
export interface OnCancel {
readonly isResolved: boolean;
readonly isRejected: boolean;
readonly isCancelled: boolean;
readonly isResolved: boolean;
readonly isRejected: boolean;
readonly isCancelled: boolean;
(cancelHandler: () => void): void;
(cancelHandler: () => void): void;
}
export class CancelablePromise<T> implements Promise<T> {
readonly [Symbol.toStringTag]!: string;
readonly [Symbol.toStringTag]!: string;
private _isResolved: boolean;
private _isRejected: boolean;
private _isCancelled: boolean;
private readonly _cancelHandlers: (() => void)[];
private readonly _promise: Promise<T>;
private _resolve?: (value: T | PromiseLike<T>) => void;
private _reject?: (reason?: any) => void;
private _isResolved: boolean;
private _isRejected: boolean;
private _isCancelled: boolean;
private readonly _cancelHandlers: (() => void)[];
private readonly _promise: Promise<T>;
private _resolve?: (value: T | PromiseLike<T>) => void;
private _reject?: (reason?: any) => void;
constructor(
executor: (
resolve: (value: T | PromiseLike<T>) => void,
reject: (reason?: any) => void,
onCancel: OnCancel
) => void
) {
this._isResolved = false;
this._isRejected = false;
this._isCancelled = false;
this._cancelHandlers = [];
this._promise = new Promise<T>((resolve, reject) => {
this._resolve = resolve;
this._reject = reject;
constructor(
executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void, onCancel: OnCancel) => void
) {
this._isResolved = false;
this._isRejected = false;
this._isCancelled = false;
this._cancelHandlers = [];
this._promise = new Promise<T>((resolve, reject) => {
this._resolve = resolve;
this._reject = reject;
const onResolve = (value: T | PromiseLike<T>): void => {
if (this._isResolved || this._isRejected || this._isCancelled) {
return;
}
this._isResolved = true;
this._resolve?.(value);
};
const onResolve = (value: T | PromiseLike<T>): void => {
if (this._isResolved || this._isRejected || this._isCancelled) {
return;
}
this._isResolved = true;
this._resolve?.(value);
};
const onReject = (reason?: any): void => {
if (this._isResolved || this._isRejected || this._isCancelled) {
return;
}
this._isRejected = true;
this._reject?.(reason);
};
const onReject = (reason?: any): void => {
if (this._isResolved || this._isRejected || this._isCancelled) {
return;
}
this._isRejected = true;
this._reject?.(reason);
};
const onCancel = (cancelHandler: () => void): void => {
if (this._isResolved || this._isRejected || this._isCancelled) {
return;
}
this._cancelHandlers.push(cancelHandler);
};
const onCancel = (cancelHandler: () => void): void => {
if (this._isResolved || this._isRejected || this._isCancelled) {
return;
}
this._cancelHandlers.push(cancelHandler);
};
Object.defineProperty(onCancel, 'isResolved', {
get: (): boolean => this._isResolved,
});
Object.defineProperty(onCancel, 'isResolved', {
get: (): boolean => this._isResolved,
});
Object.defineProperty(onCancel, 'isRejected', {
get: (): boolean => this._isRejected,
});
Object.defineProperty(onCancel, 'isRejected', {
get: (): boolean => this._isRejected,
});
Object.defineProperty(onCancel, 'isCancelled', {
get: (): boolean => this._isCancelled,
});
Object.defineProperty(onCancel, 'isCancelled', {
get: (): boolean => this._isCancelled,
});
return executor(onResolve, onReject, onCancel as OnCancel);
});
}
return executor(onResolve, onReject, onCancel as OnCancel);
});
}
public then<TResult1 = T, TResult2 = never>(
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
): Promise<TResult1 | TResult2> {
return this._promise.then(onFulfilled, onRejected);
}
public then<TResult1 = T, TResult2 = never>(
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
): Promise<TResult1 | TResult2> {
return this._promise.then(onFulfilled, onRejected);
}
public catch<TResult = never>(
onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
): Promise<T | TResult> {
return this._promise.catch(onRejected);
}
public catch<TResult = never>(
onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
): Promise<T | TResult> {
return this._promise.catch(onRejected);
}
public finally(onFinally?: (() => void) | null): Promise<T> {
return this._promise.finally(onFinally);
}
public finally(onFinally?: (() => void) | null): Promise<T> {
return this._promise.finally(onFinally);
}
public cancel(): void {
if (this._isResolved || this._isRejected || this._isCancelled) {
return;
}
this._isCancelled = true;
if (this._cancelHandlers.length) {
try {
for (const cancelHandler of this._cancelHandlers) {
cancelHandler();
}
} catch (error) {
console.warn('Cancellation threw an error', error);
return;
}
}
this._cancelHandlers.length = 0;
this._reject?.(new CancelError('Request aborted'));
}
public cancel(): void {
if (this._isResolved || this._isRejected || this._isCancelled) {
return;
}
this._isCancelled = true;
if (this._cancelHandlers.length) {
try {
for (const cancelHandler of this._cancelHandlers) {
cancelHandler();
}
} catch (error) {
console.warn('Cancellation threw an error', error);
return;
}
}
this._cancelHandlers.length = 0;
this._reject?.(new CancelError('Request aborted'));
}
public get isCancelled(): boolean {
return this._isCancelled;
}
public get isCancelled(): boolean {
return this._isCancelled;
}
}

View File

@@ -91,6 +91,7 @@ export type { OutOfDateStatusModel } from './models/OutOfDateStatusModel';
export { OutOfDateTypeModel } from './models/OutOfDateTypeModel';
export type { PackageCreateModel } from './models/PackageCreateModel';
export type { PackageDefinitionModel } from './models/PackageDefinitionModel';
export type { PackageManifestModel } from './models/PackageManifestModel';
export type { PackageMigrationStatusModel } from './models/PackageMigrationStatusModel';
export type { PackageModelBaseModel } from './models/PackageModelBaseModel';
export type { PackageUpdateModel } from './models/PackageUpdateModel';

View File

@@ -0,0 +1,10 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type PackageManifestModel = {
name?: string;
version?: string | null;
extensions?: Array<any>;
};

View File

@@ -3,6 +3,7 @@
/* eslint-disable */
import type { PackageCreateModel } from '../models/PackageCreateModel';
import type { PackageDefinitionModel } from '../models/PackageDefinitionModel';
import type { PackageManifestModel } from '../models/PackageManifestModel';
import type { PackageUpdateModel } from '../models/PackageUpdateModel';
import type { PagedPackageDefinitionModel } from '../models/PagedPackageDefinitionModel';
import type { PagedPackageMigrationStatusModel } from '../models/PagedPackageMigrationStatusModel';
@@ -166,6 +167,17 @@ export class PackageResource {
});
}
/**
* @returns any Success
* @throws ApiError
*/
public static getPackageManifest(): CancelablePromise<Array<PackageManifestModel>> {
return __request(OpenAPI, {
method: 'GET',
url: '/umbraco/management/api/v1/package/manifest',
});
}
/**
* @returns PagedPackageMigrationStatusModel Success
* @throws ApiError

View File

@@ -0,0 +1 @@
export * from './element.mixin';

View File

@@ -1,4 +1,5 @@
import config from '../../utils/rollup.config.js';
export default {
...config,
input: 'index.out.ts'
};

View File

@@ -22,7 +22,7 @@ export async function createExtensionElement(manifest: ManifestElement): Promise
console.error('-- Extension did not succeed creating an element, missing a default export of the served JavaScript file', manifest);
// If some JS was loaded and it did not at least contain a default export, then we are safe to assume that it executed as a module and does not need to be returned
// If some JS was loaded and it did not at least contain a default export, then we are safe to assume that it executed its side effects and does not need to be returned
return undefined;
}

View File

@@ -0,0 +1,8 @@
import type { UmbEntrypointModule } from "./umb-lifecycle.interface";
/**
* Validate if an ESModule exports a known init function called 'onInit'
*/
export function hasInitExport(obj: unknown): obj is Pick<UmbEntrypointModule, 'onInit'> {
return obj !== null && typeof obj === 'object' && 'onInit' in obj;
}

View File

@@ -3,6 +3,7 @@ import { UmbExtensionRegistry } from './registry/extension.registry';
export * from './registry/extension.registry';
export * from './create-extension-element.function';
export * from './has-default-export.function';
export * from './has-init-export.function';
export * from './is-manifest-element-name-type.function';
export * from './is-manifest-elementable-type.function';
export * from './is-manifest-js-type.function';
@@ -10,5 +11,6 @@ export * from './is-manifest-loader-type.function';
export * from './load-extension.function';
export * from './create-extension-element-or-fallback.function';
export * from './create-extension-class.function';
export * from './umb-lifecycle.interface';
export const umbExtensionsRegistry = new UmbExtensionRegistry();

View File

@@ -14,10 +14,10 @@ export async function loadExtension(manifest: ManifestElement): Promise<object |
if (isManifestJSType(manifest) && manifest.js) {
return await import(/* @vite-ignore */ manifest.js);
}
} catch {
console.warn('-- Extension failed to load script', manifest);
} catch (err: any) {
console.warn('-- Extension failed to load script', manifest, err);
return Promise.resolve(null);
}
return Promise.resolve(null);
}
}

View File

@@ -1,7 +1,9 @@
import { BehaviorSubject, map, Observable } from 'rxjs';
import type { ManifestTypes, ManifestTypeMap, ManifestBase, ManifestWithLoader, ManifestEntrypoint, HTMLElementConstructor } from '../../models';
import { hasDefaultExport } from '../has-default-export.function';
import { UmbContextToken } from "@umbraco-cms/context-api";
import type { UmbControllerHostInterface } from "@umbraco-cms/controller";
import type { ManifestTypes, ManifestTypeMap, ManifestBase, ManifestEntrypoint } from '../../models';
import { loadExtension } from '../load-extension.function';
import { hasInitExport } from "../has-init-export.function";
type SpecificManifestTypeOrManifestBase<T extends keyof ManifestTypeMap | string> = T extends keyof ManifestTypeMap
? ManifestTypeMap[T]
@@ -13,7 +15,7 @@ export class UmbExtensionRegistry {
private _extensions = new BehaviorSubject<Array<ManifestBase>>([]);
public readonly extensions = this._extensions.asObservable();
register(manifest: ManifestTypes): void {
register(manifest: ManifestTypes, rootHost?: UmbControllerHostInterface): void {
const extensionsValues = this._extensions.getValue();
const extension = extensionsValues.find((extension) => extension.alias === manifest.alias);
@@ -27,12 +29,9 @@ export class UmbExtensionRegistry {
// If entrypoint extension, we should load and run it immediately
if (manifest.type === 'entrypoint') {
loadExtension(manifest as ManifestEntrypoint).then((js) => {
if (hasDefaultExport<HTMLElementConstructor>(js)) {
new js.default();
} else {
console.error(
`Extension with alias '${manifest.alias}' of type 'entrypoint' must have a default export of its JavaScript module.`
);
// If the extension has an onInit export, be sure to run that or else let the module handle itself
if (hasInitExport(js)) {
js.onInit(rootHost!, this);
}
});
}
@@ -96,3 +95,5 @@ export class UmbExtensionRegistry {
) as Observable<Array<ExtensionType>>;
}
}
export const UMB_EXTENSION_REGISTRY_TOKEN = new UmbContextToken<UmbExtensionRegistry>('UmbExtensionRegistry');

View File

@@ -0,0 +1,9 @@
import type { UmbControllerHostInterface } from "@umbraco-cms/controller";
import type { UmbExtensionRegistry } from "./registry/extension.registry";
/**
* Interface containing supported life-cycle functions for ESModule entrypoints
*/
export interface UmbEntrypointModule {
onInit: (host: UmbControllerHostInterface, extensionRegistry: UmbExtensionRegistry) => void
}

View File

@@ -3,6 +3,7 @@ import {
DictionaryItemTranslationModel,
EntityTreeItemModel,
FolderTreeItemModel,
PackageManifestModel,
ProblemDetailsModel,
} from '@umbraco-cms/backend-api';
@@ -152,13 +153,9 @@ export interface SwatchDetails {
value: string;
}
export type UmbPackage = {
name?: string;
version?: string;
extensions?: unknown[];
};
export type UmbPackage = PackageManifestModel;
export type PagedManifestsResponse = UmbPackage[];
export type PackageManifestResponse = UmbPackage[];
export type UmbPackageWithMigrationStatus = UmbPackage & {
hasPendingMigrations: boolean;

View File

@@ -1,3 +1,2 @@
export * from './notification.context';
export * from './notification-handler';
export * from './layouts/default';

View File

@@ -1,7 +1,7 @@
import { html, LitElement } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { UUITextStyles } from '@umbraco-ui/uui-css';
import { ifDefined } from 'lit-html/directives/if-defined.js';
import type { UmbNotificationHandler } from '../..';
export interface UmbNotificationDefaultData {

View File

@@ -1,6 +1,6 @@
import { UUIToastNotificationElement } from '@umbraco-ui/uui';
import { v4 as uuidv4 } from 'uuid';
import type { UmbNotificationOptions, UmbNotificationData, UmbNotificationColor } from '.';
import type { UmbNotificationOptions, UmbNotificationColor, UmbNotificationDefaultData } from '.';
import './layouts/default';
@@ -12,7 +12,7 @@ export class UmbNotificationHandler {
private _closeResolver: any;
private _closePromise: Promise<any>;
private _elementName?: string;
private _data: UmbNotificationData;
private _data?: UmbNotificationDefaultData;
private _defaultColor: UmbNotificationColor = 'default';
private _defaultDuration = 6000;
@@ -28,7 +28,7 @@ export class UmbNotificationHandler {
* @param {UmbNotificationOptions} options
* @memberof UmbNotificationHandler
*/
constructor(options: UmbNotificationOptions<UmbNotificationData>) {
constructor(options: UmbNotificationOptions) {
this.key = uuidv4();
this.color = options.color || this._defaultColor;
this.duration = options.duration !== undefined ? options.duration : this._defaultDuration;

View File

@@ -2,14 +2,22 @@ import { BehaviorSubject } from 'rxjs';
import { UmbNotificationHandler } from './notification-handler';
import { UmbContextToken } from '@umbraco-cms/context-api';
export type UmbNotificationData = any;
/**
* The default data of notifications
* @export
* @interface UmbNotificationDefaultData
*/
export interface UmbNotificationDefaultData {
message: string;
headline?: string;
}
/**
* @export
* @interface UmbNotificationOptions
* @template UmbNotificationData
*/
export interface UmbNotificationOptions<UmbNotificationData> {
export interface UmbNotificationOptions<UmbNotificationData = UmbNotificationDefaultData> {
color?: UmbNotificationColor;
duration?: number | null;
elementName?: string;
@@ -29,7 +37,7 @@ export class UmbNotificationContext {
* @return {*} {UmbNotificationHandler}
* @memberof UmbNotificationContext
*/
private _open(options: UmbNotificationOptions<UmbNotificationData>): UmbNotificationHandler {
private _open(options: UmbNotificationOptions): UmbNotificationHandler {
const notificationHandler = new UmbNotificationHandler(options);
notificationHandler.element.addEventListener('closed', () => this._handleClosed(notificationHandler));
@@ -66,7 +74,7 @@ export class UmbNotificationContext {
*/
public peek(
color: UmbNotificationColor,
options: UmbNotificationOptions<UmbNotificationData>
options: UmbNotificationOptions
): UmbNotificationHandler {
return this._open({ color, ...options });
}
@@ -80,10 +88,10 @@ export class UmbNotificationContext {
*/
public stay(
color: UmbNotificationColor,
options: UmbNotificationOptions<UmbNotificationData>
options: UmbNotificationOptions
): UmbNotificationHandler {
return this._open({ ...options, color, duration: null });
}
}
export const UMB_NOTIFICATION_CONTEXT_TOKEN = new UmbContextToken<UmbNotificationContext>(UmbNotificationContext.name);
export const UMB_NOTIFICATION_CONTEXT_TOKEN = new UmbContextToken<UmbNotificationContext>('UmbNotificationContext');

View File

@@ -2,7 +2,6 @@
import {
UmbNotificationOptions,
UmbNotificationContext,
UmbNotificationDefaultData,
UMB_NOTIFICATION_CONTEXT_TOKEN,
} from '@umbraco-cms/notification';
import { ApiError, CancelablePromise, ProblemDetailsModel } from '@umbraco-cms/backend-api';
@@ -57,9 +56,9 @@ export class UmbResourceController extends UmbController {
*/
static async tryExecute<T>(promise: Promise<T>): Promise<DataSourceResponse<T>> {
try {
return { data: await promise };
return {data: await promise};
} catch (e) {
return { error: UmbResourceController.toProblemDetailsModel(e) };
return {error: UmbResourceController.toProblemDetailsModel(e)};
}
}
@@ -67,17 +66,17 @@ export class UmbResourceController extends UmbController {
* Wrap the {execute} function in a try/catch block and return the result.
* If the executor function throws an error, then show the details in a notification.
*/
async tryExecuteAndNotify<T>(options?: UmbNotificationOptions<any>): Promise<DataSourceResponse<T>> {
const { data, error } = await UmbResourceController.tryExecute<T>(this.#promise);
async tryExecuteAndNotify<T>(options?: UmbNotificationOptions): Promise<DataSourceResponse<T>> {
const {data, error} = await UmbResourceController.tryExecute<T>(this.#promise);
if (error) {
const data: UmbNotificationDefaultData = {
headline: error.title ?? 'Server Error',
message: error.detail ?? 'Something went wrong',
};
if (this.#notificationContext) {
this.#notificationContext?.peek('danger', { data, ...options });
this.#notificationContext?.peek('danger', {
data: {
headline: error.title ?? 'Server Error',
message: error.detail ?? 'Something went wrong'
}, ...options
});
} else {
console.group('UmbResourceController');
console.error(error);
@@ -85,7 +84,7 @@ export class UmbResourceController extends UmbController {
}
}
return { data, error };
return {data, error};
}
/**

View File

@@ -12,7 +12,7 @@ import { UmbRouterSlotChangeEvent, UmbRouterSlotInitEvent } from '@umbraco-cms/r
*/
@customElement('umb-router-slot')
export class UmbRouterSlotElement extends LitElement {
#router: RouterSlot;
#router: RouterSlot = new RouterSlot();
#listening = false;
@property()
@@ -37,13 +37,6 @@ export class UmbRouterSlotElement extends LitElement {
return this._routerPath + '/' + this._activeLocalPath;
}
constructor() {
super();
this.#router = new RouterSlot();
// Note: I decided not to use the local changestate event, because it is not fired when the route is changed from any router-slot. And for now I wanted to keep it local.
//this.#router.addEventListener('changestate', this._onNavigationChanged);
}
connectedCallback() {
super.connectedCallback();
if (this.#listening === false) {

View File

@@ -0,0 +1,4 @@
import config from '../../utils/rollup.config.js';
export default {
...config,
};

File diff suppressed because it is too large Load Diff

View File

@@ -29,8 +29,8 @@
"dev": "vite",
"build": "tsc && vite build --mode staging",
"build:for:static": "tsc && vite build",
"build:for:cms": "tsc && vite build -c vite.cms.config.ts",
"build:for:cms:watch": "npm run build:for:cms -- --watch",
"build:for:cms": "tsc && vite build -c vite.cms.config.ts && node utils/build-libs.js",
"build:for:cms:watch": "vite build -c vite.cms.config.ts --watch",
"preview": "vite preview --open",
"test": "web-test-runner --coverage",
"test:watch": "web-test-runner --watch",
@@ -66,7 +66,7 @@
"element-internals-polyfill": "^1.1.19",
"lit": "^2.6.1",
"lodash-es": "4.17.21",
"router-slot": "^1.5.5",
"router-slot": "file:router-slot-1.6.1.tgz",
"rxjs": "^7.8.0",
"uuid": "^9.0.0"
},
@@ -75,6 +75,7 @@
"@mdx-js/react": "^2.2.1",
"@open-wc/testing": "^3.1.7",
"@playwright/test": "^1.30.0",
"@rollup/plugin-json": "^6.0.0",
"@storybook/addon-a11y": "^7.0.0-beta.59",
"@storybook/addon-actions": "^7.0.0-beta.59",
"@storybook/addon-essentials": "^7.0.0-beta.59",

Binary file not shown.

View File

@@ -19,7 +19,7 @@ export class UmbDocumentTypeStore extends UmbStoreBase {
* @memberof UmbDocumentTypeStore
*/
constructor(host: UmbControllerHostInterface) {
super(host, UmbDocumentTypeStore.name);
super(host, UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN.toString());
}
/**
@@ -51,5 +51,5 @@ export class UmbDocumentTypeStore extends UmbStoreBase {
}
export const UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDocumentTypeStore>(
UmbDocumentTypeStore.name
'UmbDocumentTypeStore'
);

View File

@@ -21,5 +21,5 @@ export class UmbDocumentTypeTreeStore extends UmbTreeStoreBase {
}
export const UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDocumentTypeTreeStore>(
UmbDocumentTypeTreeStore.name
'UmbDocumentTypeTreeStore'
);

View File

@@ -4,6 +4,8 @@ import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
export const UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDocumentStore>('UmbDocumentStore');
/**
* @export
* @class UmbDocumentDetailStore
@@ -19,7 +21,7 @@ export class UmbDocumentStore extends UmbStoreBase {
* @memberof UmbDocumentDetailStore
*/
constructor(host: UmbControllerHostInterface) {
super(host, UmbDocumentStore.name);
super(host, UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN.toString());
}
/**
@@ -49,5 +51,3 @@ export class UmbDocumentStore extends UmbStoreBase {
this.#data.remove(uniques);
}
}
export const UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDocumentStore>(UmbDocumentStore.name);

View File

@@ -20,5 +20,5 @@ export class UmbDocumentTreeStore extends UmbTreeStoreBase {
}
export const UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDocumentTreeStore>(
UmbDocumentTreeStore.name
'UmbDocumentTreeStore'
);

View File

@@ -100,6 +100,10 @@ export class UmbDocumentWorkspaceContext
this.#activeVariantsInfo.next(activeVariants);
}
openSplitView(culture: string | null, segment: string | null) {
this.setActiveVariant(1, culture, segment);
}
getVariant(variantId: UmbVariantId) {
return this.#draft.getValue()?.variants?.find((x) => variantId.compare(x));
}

View File

@@ -14,7 +14,7 @@ export class UmbDocumentWorkspaceElement extends UmbLitElement implements UmbWor
UUITextStyles,
css`
:host {
display: block;
display: flex;
width: 100%;
height: 100%;
}

View File

@@ -160,7 +160,7 @@ export class UmbDocumentWorkspaceViewEditElement extends UmbLitElement {
this._routerPath = event.target.absoluteRouterPath;
}}
@change=${(event: UmbRouterSlotChangeEvent) => {
this._activePath = event.target.localActiveViewPath || '';
this._activePath = event.target.absoluteActiveViewPath || '';
}}>
</umb-router-slot>
`;

View File

@@ -14,7 +14,7 @@ export class UmbMediaTypeStore extends UmbStoreBase {
#data = new ArrayState<MediaTypeDetails>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {
super(host, UmbMediaTypeStore.name);
super(host, UMB_MEDIA_TYPE_STORE_CONTEXT_TOKEN.toString());
}
append(mediaType: MediaTypeDetails) {
@@ -26,4 +26,4 @@ export class UmbMediaTypeStore extends UmbStoreBase {
}
}
export const UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaTypeStore>(UmbMediaTypeStore.name);
export const UMB_MEDIA_TYPE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaTypeStore>('UmbMediaTypeStore');

View File

@@ -20,5 +20,5 @@ export class UmbMediaTypeTreeStore extends UmbTreeStoreBase {
}
export const UMB_MEDIA_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaTypeTreeStore>(
UmbMediaTypeTreeStore.name
'UmbMediaTypeTreeStore'
);

View File

@@ -19,7 +19,7 @@ export class UmbMediaStore extends UmbStoreBase {
* @memberof UmbMediaStore
*/
constructor(host: UmbControllerHostInterface) {
super(host, UmbMediaStore.name);
super(host, UMB_MEDIA_STORE_CONTEXT_TOKEN.toString());
}
/**
@@ -41,4 +41,4 @@ export class UmbMediaStore extends UmbStoreBase {
}
}
export const UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaStore>(UmbMediaStore.name);
export const UMB_MEDIA_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaStore>('UmbMediaStore');

View File

@@ -4,6 +4,8 @@ import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbTreeStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
export const UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaTreeStore>('UmbMediaTreeStore');
/**
* @export
* @class UmbMediaTreeStore
@@ -22,5 +24,3 @@ export class UmbMediaTreeStore extends UmbTreeStoreBase {
super(host, UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN.toString());
}
}
export const UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaTreeStore>(UmbMediaTreeStore.name);

View File

@@ -14,7 +14,7 @@ export class UmbMemberGroupStore extends UmbStoreBase {
#data = new ArrayState<MemberGroupDetails>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {
super(host, UmbMemberGroupStore.name);
super(host, UMB_MEMBER_GROUP_STORE_CONTEXT_TOKEN.toString());
}
append(memberGroup: MemberGroupDetails) {
@@ -26,6 +26,4 @@ export class UmbMemberGroupStore extends UmbStoreBase {
}
}
export const UMB_MEMBER_GROUP_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMemberGroupStore>(
UmbMemberGroupStore.name
);
export const UMB_MEMBER_GROUP_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMemberGroupStore>('UmbMemberGroupStore');

View File

@@ -20,5 +20,5 @@ export class UmbMemberGroupTreeStore extends UmbTreeStoreBase {
}
export const UMB_MEMBER_GROUP_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMemberGroupTreeStore>(
UmbMemberGroupTreeStore.name
'UmbMemberGroupTreeStore'
);

View File

@@ -14,7 +14,7 @@ export class UmbMemberTypeStore extends UmbStoreBase {
#data = new ArrayState<MemberTypeDetails>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {
super(host, UmbMemberTypeStore.name);
super(host, UMB_MEMBER_TYPE_STORE_CONTEXT_TOKEN.toString());
}
append(MemberType: MemberTypeDetails) {
@@ -26,6 +26,4 @@ export class UmbMemberTypeStore extends UmbStoreBase {
}
}
export const UMB_MEMBER_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMemberTypeStore>(
UmbMemberTypeStore.name
);
export const UMB_MEMBER_TYPE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMemberTypeStore>('UmbMemberTypeStore');

View File

@@ -15,5 +15,5 @@ export class UmbMemberTypeTreeStore extends UmbTreeStoreBase {
}
export const UMB_MEMBER_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMemberTypeTreeStore>(
UmbMemberTypeTreeStore.name
'UmbMemberTypeTreeStore'
);

View File

@@ -2,6 +2,8 @@ import { UmbContextToken } from '@umbraco-cms/context-api';
import { UmbTreeStoreBase } from '@umbraco-cms/store';
import type { UmbControllerHostInterface } from '@umbraco-cms/controller';
export const UMB_MEMBER_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMemberTreeStore>('UmbMemberTreeStore');
/**
* @export
* @class UmbMemberTreeStore
@@ -18,5 +20,3 @@ export class UmbMemberTreeStore extends UmbTreeStoreBase {
super(host, UMB_MEMBER_TREE_STORE_CONTEXT_TOKEN.toString());
}
}
export const UMB_MEMBER_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMemberTreeStore>(UmbMemberTreeStore.name);

View File

@@ -3,6 +3,8 @@ import { UmbPackageServerDataSource } from './sources/package.server.data';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { UmbContextConsumerController } from '@umbraco-cms/context-api';
import { ManifestBase } from '@umbraco-cms/extensions-registry';
import { isManifestJSType } from "@umbraco-cms/extensions-api";
import { OpenAPI } from "@umbraco-cms/backend-api";
// TODO: Figure out if we should base stores like this on something more generic for "collections" rather than trees.
@@ -14,6 +16,7 @@ export class UmbPackageRepository {
#init!: Promise<void>;
#packageStore?: UmbPackageStore;
#packageSource: UmbPackageServerDataSource;
#apiBaseUrl = OpenAPI.BASE;
constructor(host: UmbControllerHostInterface) {
this.#packageSource = new UmbPackageServerDataSource(host);
@@ -48,6 +51,18 @@ export class UmbPackageRepository {
// Crudely validate that the extension at least follows a basic manifest structure
// Idea: Use `Zod` to validate the manifest
if (this.isManifestBase(e)) {
/**
* Crude check to see if extension is of type "js" since it is safe to assume we do not
* need to load any other types of extensions in the backoffice (we need a js file to load)
*/
if (isManifestJSType(e)) {
// Add API base url if the js path is relative
if (!e.js.startsWith('http')) {
e.js = `${this.#apiBaseUrl}${e.js}`;
}
}
extensions.push(e);
}
});

View File

@@ -6,6 +6,8 @@ import type { ManifestBase, UmbPackage } from '@umbraco-cms/models';
import type { PackageMigrationStatusModel } from '@umbraco-cms/backend-api';
import { ArrayState } from '@umbraco-cms/observable-api';
export const UMB_PACKAGE_STORE_TOKEN = new UmbContextToken<UmbPackageStore>('UmbPackageStore');
/**
* Store for Packages
* @export
@@ -39,7 +41,7 @@ export class UmbPackageStore extends UmbStoreBase {
* @memberof PackageStore
*/
constructor(host: UmbControllerHostInterface) {
super(host, UmbPackageStore.name);
super(host, UMB_PACKAGE_STORE_TOKEN.toString());
}
/**
@@ -58,5 +60,3 @@ export class UmbPackageStore extends UmbStoreBase {
this.#migrations.append(migrations);
}
}
export const UMB_PACKAGE_STORE_TOKEN = new UmbContextToken<UmbPackageStore>(UmbPackageStore.name);

View File

@@ -1,15 +1,16 @@
import { Subject, takeUntil } from 'rxjs';
import { UmbPackageRepository } from './package.repository';
import { UmbController, UmbControllerHostInterface } from '@umbraco-cms/controller';
import { isManifestJSType, UmbExtensionRegistry } from '@umbraco-cms/extensions-api';
import { UmbExtensionRegistry } from '@umbraco-cms/extensions-api';
export class UmbServerExtensionController extends UmbController {
#host: UmbControllerHostInterface;
#unobserve = new Subject<void>();
#repository: UmbPackageRepository;
constructor(host: UmbControllerHostInterface, private readonly extensionRegistry: UmbExtensionRegistry) {
super(host, UmbServerExtensionController.name);
this.#host = host;
this.#repository = new UmbPackageRepository(host);
}
@@ -32,13 +33,7 @@ export class UmbServerExtensionController extends UmbController {
)
.subscribe((extensions) => {
extensions.forEach((extension) => {
/**
* Crude check to see if extension is of type "js" since it is safe to assume we do not
* need to load any other types of extensions in the backoffice (we need a js file to load)
*/
if (isManifestJSType(extension)) {
this.extensionRegistry.register(extension);
}
this.extensionRegistry.register(extension, this.#host);
});
});
}

View File

@@ -1,8 +1,6 @@
import { PackageResource } from '@umbraco-cms/backend-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import type { DataSourceResponse, UmbPackage } from '@umbraco-cms/models';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
import { umbracoPath } from '@umbraco-cms/utils';
/**
* Data source for packages from the server
@@ -15,11 +13,10 @@ export class UmbPackageServerDataSource {
* Get the root items from the server
* @memberof UmbPackageServerDataSource
*/
getRootItems(): Promise<DataSourceResponse<UmbPackage[]>> {
// TODO: Use real resource when available
getRootItems() {
return tryExecuteAndNotify(
this.host,
fetch(umbracoPath('/package/manifest')).then((res) => res.json())
PackageResource.getPackageManifest()
);
}

View File

@@ -35,5 +35,5 @@ export class UmbHealthCheckDashboardContext {
}
export const UMB_HEALTHCHECK_DASHBOARD_CONTEXT_TOKEN = new UmbContextToken<UmbHealthCheckDashboardContext>(
UmbHealthCheckDashboardContext.name
'UmbHealthCheckDashboardContext'
);

View File

@@ -41,4 +41,4 @@ export class UmbHealthCheckContext {
}
}
export const UMB_HEALTHCHECK_CONTEXT_TOKEN = new UmbContextToken<UmbHealthCheckContext>(UmbHealthCheckContext.name);
export const UMB_HEALTHCHECK_CONTEXT_TOKEN = new UmbContextToken<UmbHealthCheckContext>('UmbHealthCheckContext');

View File

@@ -4,6 +4,8 @@ import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
export const UMB_DATA_TYPE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDataTypeStore>('UmbDataTypeStore');
/**
* @export
* @class UmbDataTypeStore
@@ -19,7 +21,7 @@ export class UmbDataTypeStore extends UmbStoreBase {
* @memberof UmbDataTypeStore
*/
constructor(host: UmbControllerHostInterface) {
super(host, UmbDataTypeStore.name);
super(host, UMB_DATA_TYPE_STORE_CONTEXT_TOKEN.toString());
}
/**
@@ -49,5 +51,3 @@ export class UmbDataTypeStore extends UmbStoreBase {
this.#data.remove(uniques);
}
}
export const UMB_DATA_TYPE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDataTypeStore>(UmbDataTypeStore.name);

View File

@@ -21,5 +21,5 @@ export class UmbDataTypeTreeStore extends UmbTreeStoreBase {
}
export const UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDataTypeTreeStore>(
UmbDataTypeTreeStore.name
'UmbDataTypeTreeStore'
);

View File

@@ -120,7 +120,7 @@ export class UmbAppLanguageSelectElement extends UmbLitElement {
#renderTrigger() {
return html`<button id="toggle" slot="trigger" @click=${this.#onClick}>
${this._appLanguage?.name} <uui-caret></uui-caret>
${this._appLanguage?.name} <uui-symbol-expand></uui-symbol-expand>
</button>`;
}

View File

@@ -47,4 +47,4 @@ export class UmbAppLanguageContext {
}
}
export const UMB_APP_LANGUAGE_CONTEXT_TOKEN = new UmbContextToken<UmbAppLanguageContext>(UmbAppLanguageContext.name);
export const UMB_APP_LANGUAGE_CONTEXT_TOKEN = new UmbContextToken<UmbAppLanguageContext>('UmbAppLanguageContext');

View File

@@ -27,7 +27,7 @@ export class UmbLanguageRepository {
new UmbContextConsumerController(this.#host, UMB_LANGUAGE_STORE_CONTEXT_TOKEN, (instance) => {
this.#languageStore = instance;
}),
}).asPromise(),
]);
}

View File

@@ -4,6 +4,8 @@ import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { ArrayState } from '@umbraco-cms/observable-api';
import { LanguageModel } from '@umbraco-cms/backend-api';
export const UMB_LANGUAGE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbLanguageStore>('UmbLanguageStore');
/**
* @export
* @class UmbLanguageStore
@@ -15,7 +17,7 @@ export class UmbLanguageStore extends UmbStoreBase {
data = this.#data.asObservable();
constructor(host: UmbControllerHostInterface) {
super(host, UmbLanguageStore.name);
super(host, UMB_LANGUAGE_STORE_CONTEXT_TOKEN.toString());
}
append(language: LanguageModel) {
@@ -32,4 +34,3 @@ export class UmbLanguageStore extends UmbStoreBase {
}
}
export const UMB_LANGUAGE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbLanguageStore>(UmbLanguageStore.name);

View File

@@ -175,5 +175,5 @@ export class UmbCollectionContext<
}
export const UMB_COLLECTION_CONTEXT_TOKEN = new UmbContextToken<UmbCollectionContext<any, any>>(
UmbCollectionContext.name
'UmbCollectionContext'
);

View File

@@ -77,7 +77,7 @@ export class UmbBackofficeMain extends UmbLitElement {
}
private _onRouteChange = (event: UmbRouterSlotChangeEvent) => {
const currentPath = event.target.localActiveViewPath || ''
const currentPath = event.target.localActiveViewPath || '';
const section = this._sections.find((s) => this._routePrefix + s.meta.pathname === currentPath);
if (!section) return;
this._backofficeContext?.setActiveSectionAlias(section.alias);
@@ -94,11 +94,7 @@ export class UmbBackofficeMain extends UmbLitElement {
}
render() {
return html`
<umb-router-slot
.routes=${this._routes}
@change=${this._onRouteChange}
></umb-router-slot>`;
return html` <umb-router-slot .routes=${this._routes} @change=${this._onRouteChange}></umb-router-slot>`;
}
}

View File

@@ -43,5 +43,5 @@ export class UmbSectionSidebarContext {
}
export const UMB_SECTION_SIDEBAR_CONTEXT_TOKEN = new UmbContextToken<UmbSectionSidebarContext>(
UmbSectionSidebarContext.name
'UmbSectionSidebarContext'
);

View File

@@ -53,4 +53,4 @@ export class UmbSectionContext {
}
}
export const UMB_SECTION_CONTEXT_TOKEN = new UmbContextToken<UmbSectionContext>(UmbSectionContext.name);
export const UMB_SECTION_CONTEXT_TOKEN = new UmbContextToken<UmbSectionContext>('UmbSectionContext');

View File

@@ -37,7 +37,7 @@ export class UmbSectionElement extends UmbLitElement {
];
@state()
private _routes: Array<any> = [];
private _routes?: Array<any>;
@state()
private _menus?: Array<ManifestMenuSectionSidebarApp>;
@@ -45,12 +45,6 @@ export class UmbSectionElement extends UmbLitElement {
@state()
private _views?: Array<ManifestSectionView>;
@state()
private _sectionLabel = '';
@state()
private _sectionPathname = '';
private _workspaces?: Array<ManifestWorkspace>;
private _sectionContext?: UmbSectionContext;
private _sectionAlias?: string;
@@ -76,7 +70,7 @@ export class UmbSectionElement extends UmbLitElement {
this.observe(umbExtensionsRegistry.extensionsOfType('workspace'), (workspaceExtensions) => {
this._workspaces = workspaceExtensions;
this._createMenuRoutes();
this._createWorkspaceRoutes();
});
}
@@ -88,16 +82,15 @@ export class UmbSectionElement extends UmbLitElement {
.pipe(map((manifests) => manifests.filter((manifest) => manifest.meta.sections.includes(sectionAlias)))),
(manifests) => {
this._menus = manifests;
this._createMenuRoutes();
}
);
} else {
this._menus = undefined;
this._createMenuRoutes();
this._menus = [];
}
}
private _createMenuRoutes() {
private _createWorkspaceRoutes() {
if (!this._workspaces) return;
// TODO: find a way to make this reuseable across:
const workspaceRoutes = this._workspaces?.map((workspace: ManifestWorkspace) => {
return [
@@ -172,6 +165,7 @@ export class UmbSectionElement extends UmbLitElement {
}
private _createViewRoutes() {
this._routes = [];
this._routes =
this._views?.map((view) => {
return {

View File

@@ -67,7 +67,7 @@ export class UmbTreeContextMenuPageService extends UmbLitElement {
}
export const UMB_TREE_CONTEXT_MENU_PAGE_SERVICE_CONTEXT_TOKEN = new UmbContextToken<UmbTreeContextMenuPageService>(
UmbTreeContextMenuService.name
'UmbTreeContextMenuService'
);
declare global {

View File

@@ -94,7 +94,7 @@ export class UmbTreeContextMenuService extends UmbLitElement {
}
export const UMB_TREE_CONTEXT_MENU_SERVICE_CONTEXT_TOKEN = new UmbContextToken<UmbTreeContextMenuService>(
UmbTreeContextMenuService.name
'UmbTreeContextMenuService'
);
declare global {

View File

@@ -151,7 +151,13 @@ export class UmbVariantSelectorElement extends UmbLitElement {
private _switchVariant(variant: DocumentVariantModel) {
if (variant.culture === undefined || variant.segment === undefined) return;
this._variantContext?.changeVariant(variant.culture, variant.segment);
this._variantSelectorIsOpen = false;
this._close();
}
private _openSplitView(variant: DocumentVariantModel) {
if (variant.culture === undefined || variant.segment === undefined) return;
this._workspaceContext?.openSplitView(variant.culture, variant.segment);
this._close();
}
render() {
@@ -166,7 +172,7 @@ export class UmbVariantSelectorElement extends UmbLitElement {
@click=${this._toggleVariantSelector}
title=${ifDefined(this._variantTitleName)}>
${this._variantDisplayName}
<uui-caret></uui-caret>
<uui-symbol-expand></uui-symbol-expand>
</uui-button>
</div>
`
@@ -187,6 +193,8 @@ export class UmbVariantSelectorElement extends UmbLitElement {
<uui-button @click=${() => this._switchVariant(variant)}>
${variant.name} ${variant.culture} ${variant.segment}
</uui-button>
<uui-button @click=${() => this._openSplitView(variant)}> Split view </uui-button>
</li>
</ul>`
)}

View File

@@ -89,7 +89,7 @@ export class UmbWorkspaceLayout extends UmbLitElement {
private _workspaceViews: Array<ManifestWorkspaceView | ManifestWorkspaceViewCollection> = [];
@state()
private _routes: any[] = [];
private _routes?: any[];
@state()
private _routerPath?: string;
@@ -119,7 +119,7 @@ export class UmbWorkspaceLayout extends UmbLitElement {
component: () => {
if (view.type === 'workspaceViewCollection') {
return import(
'src/backoffice/shared/components/workspace/workspace-content/views/collection/workspace-view-collection.element'
'../../../../shared/components/workspace/workspace-content/views/collection/workspace-view-collection.element'
);
}
return createExtensionElement(view);
@@ -183,7 +183,7 @@ export class UmbWorkspaceLayout extends UmbLitElement {
#renderRoutes() {
return html`
${this._routes.length > 0
${this._routes && this._routes.length > 0
? html`
<umb-router-slot
id="router-slot"

View File

@@ -19,7 +19,7 @@ export class UmbTemplateStore extends UmbStoreBase {
* @memberof UmbTemplateStore
*/
constructor(host: UmbControllerHostInterface) {
super(host, UmbTemplateStore.name);
super(host, UMB_TEMPLATE_STORE_CONTEXT_TOKEN.toString());
}
/**
@@ -41,4 +41,4 @@ export class UmbTemplateStore extends UmbStoreBase {
}
}
export const UMB_TEMPLATE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbTemplateStore>(UmbTemplateStore.name);
export const UMB_TEMPLATE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbTemplateStore>('UmbTemplateStore');

View File

@@ -2,6 +2,10 @@ import { UmbContextToken } from '@umbraco-cms/context-api';
import { UmbTreeStoreBase } from '@umbraco-cms/store';
import type { UmbControllerHostInterface } from '@umbraco-cms/controller';
export const UMB_TEMPLATE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbTemplateTreeStore>(
'UmbTemplateTreeStore'
);
/**
* @export
* @class UmbTemplateTreeStore
@@ -18,7 +22,3 @@ export class UmbTemplateTreeStore extends UmbTreeStoreBase {
super(host, UMB_TEMPLATE_TREE_STORE_CONTEXT_TOKEN.toString());
}
}
export const UMB_TEMPLATE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbTemplateTreeStore>(
UmbTemplateTreeStore.name
);

View File

@@ -14,7 +14,7 @@ export class UmbDictionaryStore extends UmbStoreBase {
#data = new ArrayState<DictionaryDetails>([], (x) => x.key);
constructor(host: UmbControllerHostInterface) {
super(host, UmbDictionaryStore.name);
super(host, UMB_DICTIONARY_STORE_CONTEXT_TOKEN.toString());
}
append(dictionary: DictionaryDetails) {
@@ -26,4 +26,4 @@ export class UmbDictionaryStore extends UmbStoreBase {
}
}
export const UMB_DICTIONARY_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDictionaryStore>(UmbDictionaryStore.name);
export const UMB_DICTIONARY_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDictionaryStore>('UmbDictionaryStore');

View File

@@ -20,5 +20,5 @@ export class UmbDictionaryTreeStore extends UmbTreeStoreBase {
}
export const UMB_DICTIONARY_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbDictionaryTreeStore>(
UmbDictionaryTreeStore.name
'UmbDictionaryTreeStore'
);

View File

@@ -51,5 +51,5 @@ export class UmbCurrentUserHistoryStore {
}
export const UMB_CURRENT_USER_HISTORY_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbCurrentUserHistoryStore>(
UmbCurrentUserHistoryStore.name
'UmbCurrentUserHistoryStore'
);

View File

@@ -4,6 +4,8 @@ import type { UserDetails } from '@umbraco-cms/models';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { ObjectState } from '@umbraco-cms/observable-api';
export const UMB_CURRENT_USER_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbCurrentUserStore>('UmbCurrentUserStore');
export class UmbCurrentUserStore {
//TODO: Temp solution to get a current user. Replace when we have a real user service
private _currentUser = new ObjectState<UserDetails | undefined>(umbUsersData.getAll()[0]);
@@ -30,4 +32,3 @@ export class UmbCurrentUserStore {
}
}
export const UMB_CURRENT_USER_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbCurrentUserStore>(UmbCurrentUserStore.name);

View File

@@ -1,13 +1,14 @@
import { rest } from 'msw';
import { umbracoPath } from '@umbraco-cms/utils';
import type { PagedManifestsResponse } from '@umbraco-cms/models';
import type { PackageManifestResponse } from '@umbraco-cms/models';
export const manifestDevelopmentHandler = rest.get(umbracoPath('/package/manifest'), (_req, res, ctx) => {
return res(
// Respond with a 200 status code
ctx.status(200),
ctx.json<PagedManifestsResponse>([
ctx.json<PackageManifestResponse>([
{
name: 'Named Package',
version: '1.0.0',
@@ -71,6 +72,6 @@ export const manifestEmptyHandler = rest.get(umbracoPath('/package/manifest'), (
return res(
// Respond with a 200 status code
ctx.status(200),
ctx.json<PagedManifestsResponse>([])
ctx.json<PackageManifestResponse>([])
);
});

View File

@@ -34,7 +34,6 @@ export interface UmbModalOptions<UmbModalData> {
data?: UmbModalData;
}
// TODO: rename to UmbModalContext
// TODO: we should find a way to easily open a modal without adding custom methods to this context. It would result in a better separation of concerns.
// TODO: move all layouts into their correct "silo" folders. User picker should live with users etc.
export class UmbModalContext {
@@ -230,4 +229,4 @@ export class UmbModalContext {
}
}
export const UMB_MODAL_CONTEXT_TOKEN = new UmbContextToken<UmbModalContext>(UmbModalContext.name);
export const UMB_MODAL_CONTEXT_TOKEN = new UmbContextToken<UmbModalContext>('UmbModalContext');

View File

@@ -130,4 +130,4 @@ export class UmbInstallerContext {
}
}
export const UMB_INSTALLER_CONTEXT_TOKEN = new UmbContextToken<UmbInstallerContext>(UmbInstallerContext.name);
export const UMB_INSTALLER_CONTEXT_TOKEN = new UmbContextToken<UmbInstallerContext>('UmbInstallerContext');

View File

@@ -30,7 +30,7 @@ this.consumeContext('requestThisContextAlias', (context) => {
Or use the UmbContextToken type to define the type of the context, like this
```typescript
const contextAlias = new UmbContextToken<SomeType>('description of context for debugging purposes');
const contextAlias = new UmbContextToken<SomeType>('SomeTypeAlias', 'description of context for debugging purposes');
this.consumeContext(contextAlias, (context) => {
// Notice this is a subscription, as context might change or a new one appears, but the value is strongly typed

View File

@@ -0,0 +1,67 @@
import * as fs from 'fs';
import { exec } from 'child_process';
const libsDistFolder = '../Umbraco.Cms.StaticAssets/wwwroot/umbraco/backoffice/libs';
const libs = fs.readdirSync('./libs');
for (let i = 0; i < libs.length; i++) {
const lib = libs[i];
const libFolder = './libs/' + lib;
if (fs.statSync(libFolder).isDirectory()) {
const libPackage = libFolder + '/rollup.config.js';
if (!fs.existsSync(libPackage)) {
continue;
}
console.log('Installing ' + lib + '...');
exec('npx rollup -c rollup.config.js', { cwd: libFolder }, function (error) {
if (error) {
console.error('Error installing ' + lib + '!');
console.error(error);
} else {
console.log('Installed ' + lib + '.');
copyDistFromLib(lib, `${libFolder}/dist`);
}
});
}
}
function copyDistFromLib(libName, distPath) {
console.log(`Copying ${libName} to StaticAssets`);
const targetFolder = `${libsDistFolder}/${libName}`;
fs.cp(distPath, targetFolder, { recursive: true }, function (err) {
if (err) {
console.error(`Error copying ${libName}`);
console.error(err);
} else {
console.log(`Copied ${libName}`);
findAndCopyTypesForLib(libName);
}
});
}
/**
* Look in the ./types/libs folder for a folder with the same name as the {libName} parameter
* and copy those types into the `${libsDistFolder}/${libName}` folder.
* Wrap the types from the index.d.ts file as a new module called "@umbraco-cms/{libName}".
*/
function findAndCopyTypesForLib(libName) {
console.log('Installing types for', libName);
const typesFolder = './types/libs';
const libTypesFolder = `${typesFolder}/${libName}`;
if (fs.existsSync(libTypesFolder)) {
const libTypesTargetFolder = `${libsDistFolder}/${libName}`;
fs.cpSync(libTypesFolder, `${libTypesTargetFolder}/types`, { recursive: true });
fs.writeFileSync(`${libTypesTargetFolder}/index.d.ts`, wrapLibTypeContent(libName), {});
}
}
function wrapLibTypeContent(libName) {
return `
import * as lib from './types';
declare module "@umbraco-cms/${libName}" {
export = lib;
}`;
}

View File

@@ -1,14 +1,15 @@
import esbuild from 'rollup-plugin-esbuild';
//import { nodeResolve } from '@rollup/plugin-node-resolve';
import pluginJson from '@rollup/plugin-json';
import { nodeResolve } from '@rollup/plugin-node-resolve';
/** @type {import('rollup').RollupOptions} */
export default {
input: 'index.ts',
external: [/^@umbraco-cms\//, /^@umbraco-ui\//, /^lit/, /^rxjs/],
external: [/^@umbraco-cms\//],
output: {
file: 'dist/index.js',
format: 'es',
sourcemap: true,
},
plugins: [esbuild({ sourceMap: true })],
plugins: [nodeResolve(), pluginJson(), esbuild({ sourceMap: true })],
};