Merge branch 'main' into bugfix/rename-file-system-file
This commit is contained in:
44
src/Umbraco.Web.UI.Client/devops/build/check-path-length.js
Normal file
44
src/Umbraco.Web.UI.Client/devops/build/check-path-length.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import { readdirSync, statSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
const PROJECT_DIR = process.argv[2] ?? '.';
|
||||
const MAX_PATH_LENGTH = process.argv[3] ?? 140;
|
||||
const IS_CI = process.env.CI === 'true';
|
||||
const IS_AZURE_PIPELINES = process.env.TF_BUILD === 'true';
|
||||
const IS_GITHUB_ACTIONS = process.env.GITHUB_ACTIONS === 'true';
|
||||
const FILE_PATH_COLOR = '\x1b[36m%s\x1b[0m';
|
||||
|
||||
console.log(`Checking path length in ${PROJECT_DIR} for paths exceeding ${MAX_PATH_LENGTH}...`);
|
||||
console.log('CI detected:', IS_CI);
|
||||
|
||||
console.log('\n-----------------------------------');
|
||||
console.log('Results:');
|
||||
console.log('-----------------------------------\n');
|
||||
|
||||
function checkPathLength(dir) {
|
||||
const files = readdirSync(dir);
|
||||
|
||||
files.forEach(file => {
|
||||
const filePath = join(dir, file);
|
||||
if (filePath.length > MAX_PATH_LENGTH) {
|
||||
|
||||
if (IS_CI) {
|
||||
//process.exitCode = 1; // TODO: Uncomment this line to fail the build
|
||||
}
|
||||
|
||||
if (IS_AZURE_PIPELINES) {
|
||||
console.error(`##vso[task.logissue type=warning;sourcepath=${filePath};]Path exceeds maximum length of ${MAX_PATH_LENGTH} characters: ${filePath} with ${filePath.length} characters`);
|
||||
} else if (IS_GITHUB_ACTIONS) {
|
||||
console.error(`::warning file=${filePath},title=Path exceeds ${MAX_PATH_LENGTH} characters::Paths should not be longer than ${MAX_PATH_LENGTH} characters to support WIN32 systems. The file ${filePath} exceeds that with ${filePath.length - MAX_PATH_LENGTH} characters.`);
|
||||
} else {
|
||||
console.error(`Path exceeds maximum length of ${MAX_PATH_LENGTH} characters: ${FILE_PATH_COLOR}`, filePath, filePath.length - MAX_PATH_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
if (statSync(filePath).isDirectory()) {
|
||||
checkPathLength(filePath, MAX_PATH_LENGTH);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
checkPathLength(PROJECT_DIR, MAX_PATH_LENGTH);
|
||||
@@ -39,11 +39,7 @@ export class ExampleDatasetDashboard extends UmbElementMixin(LitElement) {
|
||||
},
|
||||
{
|
||||
alias: 'items',
|
||||
value: {
|
||||
0: { sortOrder: 1, value: 'First Option' },
|
||||
1: { sortOrder: 2, value: 'Second Option' },
|
||||
2: { sortOrder: 3, value: 'Third Option' },
|
||||
},
|
||||
value: [ 'First Option' , 'Second Option', 'Third Option' ],
|
||||
},
|
||||
]}
|
||||
property-editor-ui-alias="Umb.PropertyEditorUi.Dropdown"></umb-property>
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
"./user-permission": "./dist-cms/packages/user/user-permission/index.js",
|
||||
"./user": "./dist-cms/packages/user/user/index.js",
|
||||
"./utils": "./dist-cms/packages/core/utils/index.js",
|
||||
"./validation": "./dist-cms/packages/core/validation/index.js",
|
||||
"./variant": "./dist-cms/packages/core/variant/index.js",
|
||||
"./webhook": "./dist-cms/packages/webhook/index.js",
|
||||
"./workspace": "./dist-cms/packages/core/workspace/index.js",
|
||||
@@ -122,8 +123,9 @@
|
||||
"build:for:cms": "npm run build && node ./devops/build/copy-to-cms.js",
|
||||
"build:for:static": "vite build",
|
||||
"build:vite": "tsc && vite build --mode staging",
|
||||
"build": "tsc --project ./src/tsconfig.build.json && rollup -c ./src/rollup.config.js && npm run package:validate && npm run generate:manifest",
|
||||
"build": "tsc --project ./src/tsconfig.build.json && rollup -c ./src/rollup.config.js && npm run package:validate && npm run generate:manifest && npm run check:paths",
|
||||
"check": "npm run lint:errors && npm run compile && npm run build-storybook && npm run generate:jsonschema:dist",
|
||||
"check:paths": "node ./devops/build/check-path-length.js src 140",
|
||||
"compile": "tsc",
|
||||
"dev": "vite",
|
||||
"dev:server": "VITE_UMBRACO_USE_MSW=off vite",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { UmbBackofficeContext } from './backoffice.context.js';
|
||||
import { UmbServerExtensionRegistrator } from './server-extension-registrator.controller.js';
|
||||
import { css, html, customElement } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import {
|
||||
UmbBundleExtensionInitializer,
|
||||
UmbEntryPointExtensionInitializer,
|
||||
UmbServerExtensionRegistrator,
|
||||
} from '@umbraco-cms/backoffice/extension-api';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
|
||||
@@ -55,7 +55,7 @@ export class UmbBackofficeElement extends UmbLitElement {
|
||||
new UmbBackofficeContext(this);
|
||||
new UmbBundleExtensionInitializer(this, umbExtensionsRegistry);
|
||||
new UmbEntryPointExtensionInitializer(this, umbExtensionsRegistry);
|
||||
new UmbServerExtensionRegistrator(this, umbExtensionsRegistry);
|
||||
new UmbServerExtensionRegistrator(this, umbExtensionsRegistry).registerAllExtensions();
|
||||
|
||||
// So far local packages are this simple to registerer, so no need for a manager to do that:
|
||||
CORE_PACKAGES.forEach(async (packageImport) => {
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import { PackageResource, OpenAPI } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { UmbBackofficeExtensionRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
import type { ManifestBase } from '@umbraco-cms/backoffice/extension-api';
|
||||
import { isManifestBaseType } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
// TODO: consider if this can be replaced by the new extension controllers
|
||||
export class UmbServerExtensionRegistrator extends UmbControllerBase {
|
||||
#extensionRegistry: UmbBackofficeExtensionRegistry;
|
||||
#apiBaseUrl = OpenAPI.BASE;
|
||||
|
||||
constructor(host: UmbControllerHost, extensionRegistry: UmbBackofficeExtensionRegistry) {
|
||||
super(host, UmbServerExtensionRegistrator.name);
|
||||
this.#extensionRegistry = extensionRegistry;
|
||||
this.#loadServerPackages();
|
||||
}
|
||||
|
||||
async #loadServerPackages() {
|
||||
/* TODO: we need a new endpoint here, to remove the dependency on the package repository, to get the modules available for the backoffice scope
|
||||
/ we will need a similar endpoint for the login, installer etc at some point.
|
||||
We should expose more information about the packages when not authorized so the end point should only return a list of modules from the manifest with
|
||||
with the correct scope.
|
||||
|
||||
This code is copy pasted from the package repository. We probably don't need this is the package repository anymore.
|
||||
*/
|
||||
const { data: packages } = await tryExecuteAndNotify(this, PackageResource.getPackageManifest());
|
||||
|
||||
if (packages) {
|
||||
// Append packages to the store but only if they have a name
|
||||
//store.appendItems(packages.filter((p) => p.name?.length));
|
||||
const extensions: ManifestBase[] = [];
|
||||
|
||||
packages.forEach((p) => {
|
||||
p.extensions?.forEach((e) => {
|
||||
// Crudely validate that the extension at least follows a basic manifest structure
|
||||
// Idea: Use `Zod` to validate the manifest
|
||||
if (isManifestBaseType(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)
|
||||
*/
|
||||
|
||||
// TODO: add helper to check for relative paths
|
||||
// Add base url if the js path is relative
|
||||
if ('js' in e && typeof e.js === 'string' && !e.js.startsWith('http')) {
|
||||
e.js = `${this.#apiBaseUrl}${e.js}`;
|
||||
}
|
||||
|
||||
// Add base url if the element path is relative
|
||||
if ('element' in e && typeof e.element === 'string' && !e.element.startsWith('http')) {
|
||||
e.element = `${this.#apiBaseUrl}${e.element}`;
|
||||
}
|
||||
|
||||
// Add base url if the element path api relative
|
||||
if ('api' in e && typeof e.api === 'string' && !e.api.startsWith('http')) {
|
||||
e.api = `${this.#apiBaseUrl}${e.api}`;
|
||||
}
|
||||
|
||||
extensions.push(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.#extensionRegistry.registerMany(extensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import type { ItemReferenceByIdResponseModel } from './ItemReferenceByIdResponse
|
||||
|
||||
export type RecycleBinItemResponseModelBaseModel = {
|
||||
id: string;
|
||||
type: string;
|
||||
hasChildren: boolean;
|
||||
parent?: ItemReferenceByIdResponseModel | null;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { DatatypeConfigurationResponseModel } from '../models/DatatypeConfi
|
||||
import type { DataTypeItemResponseModel } from '../models/DataTypeItemResponseModel';
|
||||
import type { DataTypeReferenceResponseModel } from '../models/DataTypeReferenceResponseModel';
|
||||
import type { DataTypeResponseModel } from '../models/DataTypeResponseModel';
|
||||
import type { DataTypeTreeItemResponseModel } from '../models/DataTypeTreeItemResponseModel';
|
||||
import type { FolderResponseModel } from '../models/FolderResponseModel';
|
||||
import type { MoveDataTypeRequestModel } from '../models/MoveDataTypeRequestModel';
|
||||
import type { PagedDataTypeItemResponseModel } from '../models/PagedDataTypeItemResponseModel';
|
||||
@@ -382,6 +383,27 @@ export class DataTypeResource {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns any Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getTreeDataTypeAncestors({
|
||||
descendantId,
|
||||
}: {
|
||||
descendantId?: string,
|
||||
}): CancelablePromise<Array<DataTypeTreeItemResponseModel>> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/tree/data-type/ancestors',
|
||||
query: {
|
||||
'descendantId': descendantId,
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns PagedDataTypeTreeItemResponseModel Success
|
||||
* @throws ApiError
|
||||
|
||||
@@ -3,12 +3,19 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { CreateDictionaryItemRequestModel } from '../models/CreateDictionaryItemRequestModel';
|
||||
import type { DataTypeTreeItemResponseModel } from '../models/DataTypeTreeItemResponseModel';
|
||||
import type { DictionaryItemItemResponseModel } from '../models/DictionaryItemItemResponseModel';
|
||||
import type { DictionaryItemResponseModel } from '../models/DictionaryItemResponseModel';
|
||||
import type { DocumentBlueprintTreeItemResponseModel } from '../models/DocumentBlueprintTreeItemResponseModel';
|
||||
import type { DocumentTypeTreeItemResponseModel } from '../models/DocumentTypeTreeItemResponseModel';
|
||||
import type { FolderTreeItemResponseModel } from '../models/FolderTreeItemResponseModel';
|
||||
import type { ImportDictionaryRequestModel } from '../models/ImportDictionaryRequestModel';
|
||||
import type { MediaTypeTreeItemResponseModel } from '../models/MediaTypeTreeItemResponseModel';
|
||||
import type { MoveDictionaryRequestModel } from '../models/MoveDictionaryRequestModel';
|
||||
import type { NamedEntityTreeItemResponseModel } from '../models/NamedEntityTreeItemResponseModel';
|
||||
import type { PagedDictionaryOverviewResponseModel } from '../models/PagedDictionaryOverviewResponseModel';
|
||||
import type { PagedNamedEntityTreeItemResponseModel } from '../models/PagedNamedEntityTreeItemResponseModel';
|
||||
import type { RelationTypeTreeItemResponseModel } from '../models/RelationTypeTreeItemResponseModel';
|
||||
import type { UpdateDictionaryItemRequestModel } from '../models/UpdateDictionaryItemRequestModel';
|
||||
|
||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||
@@ -243,6 +250,27 @@ export class DictionaryResource {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns any Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getTreeDictionaryAncestors({
|
||||
descendantId,
|
||||
}: {
|
||||
descendantId?: string,
|
||||
}): CancelablePromise<Array<(NamedEntityTreeItemResponseModel | DataTypeTreeItemResponseModel | DocumentBlueprintTreeItemResponseModel | DocumentTypeTreeItemResponseModel | FolderTreeItemResponseModel | MediaTypeTreeItemResponseModel | RelationTypeTreeItemResponseModel)>> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/tree/dictionary/ancestors',
|
||||
query: {
|
||||
'descendantId': descendantId,
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns PagedNamedEntityTreeItemResponseModel Success
|
||||
* @throws ApiError
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { DocumentConfigurationResponseModel } from '../models/DocumentConfi
|
||||
import type { DocumentItemResponseModel } from '../models/DocumentItemResponseModel';
|
||||
import type { DocumentNotificationResponseModel } from '../models/DocumentNotificationResponseModel';
|
||||
import type { DocumentResponseModel } from '../models/DocumentResponseModel';
|
||||
import type { DocumentTreeItemResponseModel } from '../models/DocumentTreeItemResponseModel';
|
||||
import type { DomainsResponseModel } from '../models/DomainsResponseModel';
|
||||
import type { MoveDocumentRequestModel } from '../models/MoveDocumentRequestModel';
|
||||
import type { MoveMediaRequestModel } from '../models/MoveMediaRequestModel';
|
||||
@@ -807,6 +808,27 @@ export class DocumentResource {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns any Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getTreeDocumentAncestors({
|
||||
descendantId,
|
||||
}: {
|
||||
descendantId?: string,
|
||||
}): CancelablePromise<Array<DocumentTreeItemResponseModel>> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/tree/document/ancestors',
|
||||
query: {
|
||||
'descendantId': descendantId,
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns PagedDocumentTreeItemResponseModel Success
|
||||
* @throws ApiError
|
||||
|
||||
@@ -11,6 +11,7 @@ import type { DocumentTypeCompositionResponseModel } from '../models/DocumentTyp
|
||||
import type { DocumentTypeConfigurationResponseModel } from '../models/DocumentTypeConfigurationResponseModel';
|
||||
import type { DocumentTypeItemResponseModel } from '../models/DocumentTypeItemResponseModel';
|
||||
import type { DocumentTypeResponseModel } from '../models/DocumentTypeResponseModel';
|
||||
import type { DocumentTypeTreeItemResponseModel } from '../models/DocumentTypeTreeItemResponseModel';
|
||||
import type { FolderResponseModel } from '../models/FolderResponseModel';
|
||||
import type { MoveDocumentTypeRequestModel } from '../models/MoveDocumentTypeRequestModel';
|
||||
import type { PagedAllowedDocumentTypeModel } from '../models/PagedAllowedDocumentTypeModel';
|
||||
@@ -405,6 +406,27 @@ export class DocumentTypeResource {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns any Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getTreeDocumentTypeAncestors({
|
||||
descendantId,
|
||||
}: {
|
||||
descendantId?: string,
|
||||
}): CancelablePromise<Array<DocumentTypeTreeItemResponseModel>> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/tree/document-type/ancestors',
|
||||
query: {
|
||||
'descendantId': descendantId,
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns PagedDocumentTypeTreeItemResponseModel Success
|
||||
* @throws ApiError
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { DirectionModel } from '../models/DirectionModel';
|
||||
import type { MediaConfigurationResponseModel } from '../models/MediaConfigurationResponseModel';
|
||||
import type { MediaItemResponseModel } from '../models/MediaItemResponseModel';
|
||||
import type { MediaResponseModel } from '../models/MediaResponseModel';
|
||||
import type { MediaTreeItemResponseModel } from '../models/MediaTreeItemResponseModel';
|
||||
import type { MoveMediaRequestModel } from '../models/MoveMediaRequestModel';
|
||||
import type { PagedMediaCollectionResponseModel } from '../models/PagedMediaCollectionResponseModel';
|
||||
import type { PagedMediaRecycleBinItemResponseModel } from '../models/PagedMediaRecycleBinItemResponseModel';
|
||||
@@ -474,6 +475,27 @@ export class MediaResource {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns any Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getTreeMediaAncestors({
|
||||
descendantId,
|
||||
}: {
|
||||
descendantId?: string,
|
||||
}): CancelablePromise<Array<MediaTreeItemResponseModel>> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/tree/media/ancestors',
|
||||
query: {
|
||||
'descendantId': descendantId,
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns PagedMediaTreeItemResponseModel Success
|
||||
* @throws ApiError
|
||||
|
||||
@@ -11,6 +11,7 @@ import type { MediaTypeCompositionRequestModel } from '../models/MediaTypeCompos
|
||||
import type { MediaTypeCompositionResponseModel } from '../models/MediaTypeCompositionResponseModel';
|
||||
import type { MediaTypeItemResponseModel } from '../models/MediaTypeItemResponseModel';
|
||||
import type { MediaTypeResponseModel } from '../models/MediaTypeResponseModel';
|
||||
import type { MediaTypeTreeItemResponseModel } from '../models/MediaTypeTreeItemResponseModel';
|
||||
import type { MoveMediaTypeRequestModel } from '../models/MoveMediaTypeRequestModel';
|
||||
import type { PagedAllowedMediaTypeModel } from '../models/PagedAllowedMediaTypeModel';
|
||||
import type { PagedMediaTypeTreeItemResponseModel } from '../models/PagedMediaTypeTreeItemResponseModel';
|
||||
@@ -390,6 +391,27 @@ export class MediaTypeResource {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns any Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getTreeMediaTypeAncestors({
|
||||
descendantId,
|
||||
}: {
|
||||
descendantId?: string,
|
||||
}): CancelablePromise<Array<MediaTypeTreeItemResponseModel>> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/tree/media-type/ancestors',
|
||||
query: {
|
||||
'descendantId': descendantId,
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns PagedMediaTypeTreeItemResponseModel Success
|
||||
* @throws ApiError
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
/* eslint-disable */
|
||||
import type { CreatePartialViewFolderRequestModel } from '../models/CreatePartialViewFolderRequestModel';
|
||||
import type { CreatePartialViewRequestModel } from '../models/CreatePartialViewRequestModel';
|
||||
import type { FileSystemTreeItemPresentationModel } from '../models/FileSystemTreeItemPresentationModel';
|
||||
import type { PagedFileSystemTreeItemPresentationModel } from '../models/PagedFileSystemTreeItemPresentationModel';
|
||||
import type { PagedPartialViewSnippetItemResponseModel } from '../models/PagedPartialViewSnippetItemResponseModel';
|
||||
import type { PartialViewFolderResponseModel } from '../models/PartialViewFolderResponseModel';
|
||||
@@ -280,6 +281,27 @@ export class PartialViewResource {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns any Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getTreePartialViewAncestors({
|
||||
descendantPath,
|
||||
}: {
|
||||
descendantPath?: string,
|
||||
}): CancelablePromise<Array<FileSystemTreeItemPresentationModel>> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/tree/partial-view/ancestors',
|
||||
query: {
|
||||
'descendantPath': descendantPath,
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns PagedFileSystemTreeItemPresentationModel Success
|
||||
* @throws ApiError
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
/* eslint-disable */
|
||||
import type { CreateScriptFolderRequestModel } from '../models/CreateScriptFolderRequestModel';
|
||||
import type { CreateScriptRequestModel } from '../models/CreateScriptRequestModel';
|
||||
import type { FileSystemTreeItemPresentationModel } from '../models/FileSystemTreeItemPresentationModel';
|
||||
import type { PagedFileSystemTreeItemPresentationModel } from '../models/PagedFileSystemTreeItemPresentationModel';
|
||||
import type { RenameScriptRequestModel } from '../models/RenameScriptRequestModel';
|
||||
import type { ScriptFolderResponseModel } from '../models/ScriptFolderResponseModel';
|
||||
@@ -232,6 +233,27 @@ export class ScriptResource {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns any Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getTreeScriptAncestors({
|
||||
descendantPath,
|
||||
}: {
|
||||
descendantPath?: string,
|
||||
}): CancelablePromise<Array<FileSystemTreeItemPresentationModel>> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/tree/script/ancestors',
|
||||
query: {
|
||||
'descendantPath': descendantPath,
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns PagedFileSystemTreeItemPresentationModel Success
|
||||
* @throws ApiError
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { FileSystemTreeItemPresentationModel } from '../models/FileSystemTreeItemPresentationModel';
|
||||
import type { PagedFileSystemTreeItemPresentationModel } from '../models/PagedFileSystemTreeItemPresentationModel';
|
||||
import type { StaticFileItemResponseModel } from '../models/StaticFileItemResponseModel';
|
||||
|
||||
@@ -32,6 +33,27 @@ export class StaticFileResource {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns any Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getTreeStaticFileAncestors({
|
||||
descendantPath,
|
||||
}: {
|
||||
descendantPath?: string,
|
||||
}): CancelablePromise<Array<FileSystemTreeItemPresentationModel>> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/tree/static-file/ancestors',
|
||||
query: {
|
||||
'descendantPath': descendantPath,
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns PagedFileSystemTreeItemPresentationModel Success
|
||||
* @throws ApiError
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
/* eslint-disable */
|
||||
import type { CreateStylesheetFolderRequestModel } from '../models/CreateStylesheetFolderRequestModel';
|
||||
import type { CreateStylesheetRequestModel } from '../models/CreateStylesheetRequestModel';
|
||||
import type { FileSystemTreeItemPresentationModel } from '../models/FileSystemTreeItemPresentationModel';
|
||||
import type { PagedFileSystemTreeItemPresentationModel } from '../models/PagedFileSystemTreeItemPresentationModel';
|
||||
import type { RenameStylesheetRequestModel } from '../models/RenameStylesheetRequestModel';
|
||||
import type { StylesheetFolderResponseModel } from '../models/StylesheetFolderResponseModel';
|
||||
@@ -232,6 +233,27 @@ export class StylesheetResource {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns any Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getTreeStylesheetAncestors({
|
||||
descendantPath,
|
||||
}: {
|
||||
descendantPath?: string,
|
||||
}): CancelablePromise<Array<FileSystemTreeItemPresentationModel>> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/tree/stylesheet/ancestors',
|
||||
query: {
|
||||
'descendantPath': descendantPath,
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns PagedFileSystemTreeItemPresentationModel Success
|
||||
* @throws ApiError
|
||||
|
||||
@@ -3,7 +3,14 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { CreateTemplateRequestModel } from '../models/CreateTemplateRequestModel';
|
||||
import type { DataTypeTreeItemResponseModel } from '../models/DataTypeTreeItemResponseModel';
|
||||
import type { DocumentBlueprintTreeItemResponseModel } from '../models/DocumentBlueprintTreeItemResponseModel';
|
||||
import type { DocumentTypeTreeItemResponseModel } from '../models/DocumentTypeTreeItemResponseModel';
|
||||
import type { FolderTreeItemResponseModel } from '../models/FolderTreeItemResponseModel';
|
||||
import type { MediaTypeTreeItemResponseModel } from '../models/MediaTypeTreeItemResponseModel';
|
||||
import type { NamedEntityTreeItemResponseModel } from '../models/NamedEntityTreeItemResponseModel';
|
||||
import type { PagedNamedEntityTreeItemResponseModel } from '../models/PagedNamedEntityTreeItemResponseModel';
|
||||
import type { RelationTypeTreeItemResponseModel } from '../models/RelationTypeTreeItemResponseModel';
|
||||
import type { TemplateConfigurationResponseModel } from '../models/TemplateConfigurationResponseModel';
|
||||
import type { TemplateItemResponseModel } from '../models/TemplateItemResponseModel';
|
||||
import type { TemplateQueryExecuteModel } from '../models/TemplateQueryExecuteModel';
|
||||
@@ -184,6 +191,27 @@ export class TemplateResource {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns any Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getTreeTemplateAncestors({
|
||||
descendantId,
|
||||
}: {
|
||||
descendantId?: string,
|
||||
}): CancelablePromise<Array<(NamedEntityTreeItemResponseModel | DataTypeTreeItemResponseModel | DocumentBlueprintTreeItemResponseModel | DocumentTypeTreeItemResponseModel | FolderTreeItemResponseModel | MediaTypeTreeItemResponseModel | RelationTypeTreeItemResponseModel)>> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/tree/template/ancestors',
|
||||
query: {
|
||||
'descendantId': descendantId,
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns PagedNamedEntityTreeItemResponseModel Success
|
||||
* @throws ApiError
|
||||
|
||||
@@ -8,3 +8,4 @@ export * from './extension-manifest-initializer.controller.js';
|
||||
export * from './extensions-manifest-initializer.controller.js';
|
||||
export * from './extension-element-and-api-initializer.controller.js';
|
||||
export * from './extensions-element-and-api-initializer.controller.js';
|
||||
export * from './server-extension-registrator.controller.js';
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import type { ManifestBase } from '../types/index.js';
|
||||
import { isManifestBaseType } from '../type-guards/index.js';
|
||||
import {
|
||||
PackageResource,
|
||||
OpenAPI,
|
||||
type PackageManifestResponseModel,
|
||||
} from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { UmbBackofficeExtensionRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
|
||||
// TODO: consider if this can be replaced by the new extension controllers
|
||||
export class UmbServerExtensionRegistrator extends UmbControllerBase {
|
||||
#extensionRegistry: UmbBackofficeExtensionRegistry;
|
||||
#apiBaseUrl = OpenAPI.BASE;
|
||||
|
||||
constructor(host: UmbControllerHost, extensionRegistry: UmbBackofficeExtensionRegistry) {
|
||||
super(host, UmbServerExtensionRegistrator.name);
|
||||
this.#extensionRegistry = extensionRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all extensions from the server.
|
||||
* This is used to register all extensions that are available to the user (including private extensions).
|
||||
* @remark Users must have the BACKOFFICE_ACCESS permission to access this method.
|
||||
*/
|
||||
public async registerAllExtensions() {
|
||||
const { data: packages } = await tryExecuteAndNotify(this, PackageResource.getPackageManifest());
|
||||
if (packages) {
|
||||
await this.#loadServerPackages(packages);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all public extensions from the server.
|
||||
* This is used to register all extensions that are available to the user (excluding private extensions) such as login extensions.
|
||||
* @remark Any user can access this method without any permissions.
|
||||
*/
|
||||
public async registerPublicExtensions() {
|
||||
const { data: packages } = await tryExecuteAndNotify(this, PackageResource.getPackageManifestPublic());
|
||||
if (packages) {
|
||||
await this.#loadServerPackages(packages);
|
||||
}
|
||||
}
|
||||
|
||||
async #loadServerPackages(packages: PackageManifestResponseModel[]) {
|
||||
const extensions: ManifestBase[] = [];
|
||||
|
||||
packages.forEach((p) => {
|
||||
p.extensions?.forEach((e) => {
|
||||
// Crudely validate that the extension at least follows a basic manifest structure
|
||||
// Idea: Use `Zod` to validate the manifest
|
||||
if (isManifestBaseType(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)
|
||||
*/
|
||||
|
||||
// TODO: add helper to check for relative paths
|
||||
// Add base url if the js path is relative
|
||||
if ('js' in e && typeof e.js === 'string' && !e.js.startsWith('http')) {
|
||||
e.js = `${this.#apiBaseUrl}${e.js}`;
|
||||
}
|
||||
|
||||
// Add base url if the element path is relative
|
||||
if ('element' in e && typeof e.element === 'string' && !e.element.startsWith('http')) {
|
||||
e.element = `${this.#apiBaseUrl}${e.element}`;
|
||||
}
|
||||
|
||||
// Add base url if the element path api relative
|
||||
if ('api' in e && typeof e.api === 'string' && !e.api.startsWith('http')) {
|
||||
e.api = `${this.#apiBaseUrl}${e.api}`;
|
||||
}
|
||||
|
||||
extensions.push(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.#extensionRegistry.registerMany(extensions);
|
||||
}
|
||||
}
|
||||
@@ -399,11 +399,7 @@ export const data: Array<UmbMockDataTypeModel> = [
|
||||
},
|
||||
{
|
||||
alias: 'items',
|
||||
value: {
|
||||
0: { sortOrder: 1, value: 'First Option' },
|
||||
1: { sortOrder: 2, value: 'Second Option' },
|
||||
2: { sortOrder: 3, value: 'I Am the third Option' },
|
||||
},
|
||||
value: ['First Option', 'Second Option', 'I Am the third Option'],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -519,11 +515,7 @@ export const data: Array<UmbMockDataTypeModel> = [
|
||||
values: [
|
||||
{
|
||||
alias: 'items',
|
||||
value: {
|
||||
0: { sortOrder: 1, value: 'First Option' },
|
||||
1: { sortOrder: 2, value: 'Second Option' },
|
||||
2: { sortOrder: 3, value: 'I Am the third Option' },
|
||||
},
|
||||
value: ['First Option', 'Second Option', 'I Am the third Option'],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -540,11 +532,7 @@ export const data: Array<UmbMockDataTypeModel> = [
|
||||
values: [
|
||||
{
|
||||
alias: 'items',
|
||||
value: {
|
||||
0: { sortOrder: 1, value: 'First Option' },
|
||||
1: { sortOrder: 2, value: 'Second Option' },
|
||||
2: { sortOrder: 3, value: 'I Am the third Option' },
|
||||
},
|
||||
value: ['First Option', 'Second Option', 'I Am the third Option'],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import type { UmbBlockGridTypeAreaType } from '../../../types.js';
|
||||
import type { UmbPropertyDatasetContext } from '@umbraco-cms/backoffice/property';
|
||||
import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property';
|
||||
import type {
|
||||
UmbInvariantableWorkspaceContextInterface,
|
||||
UmbWorkspaceContextInterface,
|
||||
} from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbInvariantDatasetWorkspaceContext, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
import {
|
||||
UmbEditableWorkspaceContextBase,
|
||||
UmbSaveableWorkspaceContextBase,
|
||||
UmbInvariantWorkspacePropertyDatasetContext,
|
||||
} from '@umbraco-cms/backoffice/workspace';
|
||||
import { UmbArrayState, UmbObjectState, appendToFrozenArray } from '@umbraco-cms/backoffice/observable-api';
|
||||
@@ -15,8 +12,8 @@ import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { ManifestWorkspace, PropertyEditorSettingsProperty } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export class UmbBlockGridAreaTypeWorkspaceContext
|
||||
extends UmbEditableWorkspaceContextBase<UmbBlockGridTypeAreaType>
|
||||
implements UmbInvariantableWorkspaceContextInterface
|
||||
extends UmbSaveableWorkspaceContextBase<UmbBlockGridTypeAreaType>
|
||||
implements UmbInvariantDatasetWorkspaceContext
|
||||
{
|
||||
// Just for context token safety:
|
||||
public readonly IS_BLOCK_GRID_AREA_TYPE_WORKSPACE_CONTEXT = true;
|
||||
@@ -132,7 +129,7 @@ export class UmbBlockGridAreaTypeWorkspaceContext
|
||||
export default UmbBlockGridAreaTypeWorkspaceContext;
|
||||
|
||||
export const UMB_BLOCK_GRID_AREA_TYPE_WORKSPACE_CONTEXT = new UmbContextToken<
|
||||
UmbWorkspaceContextInterface,
|
||||
UmbWorkspaceContext,
|
||||
UmbBlockGridAreaTypeWorkspaceContext
|
||||
>(
|
||||
'UmbWorkspaceContext',
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import type { UmbBlockTypeWorkspaceContext } from './block-type-workspace.context.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { UmbWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
|
||||
export const UMB_BLOCK_TYPE_WORKSPACE_CONTEXT = new UmbContextToken<
|
||||
UmbWorkspaceContextInterface,
|
||||
UmbBlockTypeWorkspaceContext
|
||||
>(
|
||||
export const UMB_BLOCK_TYPE_WORKSPACE_CONTEXT = new UmbContextToken<UmbWorkspaceContext, UmbBlockTypeWorkspaceContext>(
|
||||
'UmbWorkspaceContext',
|
||||
undefined,
|
||||
(context): context is UmbBlockTypeWorkspaceContext => (context as any).IS_BLOCK_TYPE_WORKSPACE_CONTEXT,
|
||||
|
||||
@@ -3,11 +3,11 @@ import { UmbBlockTypeWorkspaceEditorElement } from './block-type-workspace-edito
|
||||
import type { UmbPropertyDatasetContext } from '@umbraco-cms/backoffice/property';
|
||||
import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property';
|
||||
import type {
|
||||
UmbInvariantableWorkspaceContextInterface,
|
||||
UmbInvariantDatasetWorkspaceContext,
|
||||
UmbRoutableWorkspaceContext,
|
||||
} from '@umbraco-cms/backoffice/workspace';
|
||||
import {
|
||||
UmbEditableWorkspaceContextBase,
|
||||
UmbSaveableWorkspaceContextBase,
|
||||
UmbInvariantWorkspacePropertyDatasetContext,
|
||||
UmbWorkspaceIsNewRedirectController,
|
||||
UmbWorkspaceRouteManager,
|
||||
@@ -17,8 +17,8 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { ManifestWorkspace, PropertyEditorSettingsProperty } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export class UmbBlockTypeWorkspaceContext<BlockTypeData extends UmbBlockTypeWithGroupKey = UmbBlockTypeWithGroupKey>
|
||||
extends UmbEditableWorkspaceContextBase<BlockTypeData>
|
||||
implements UmbInvariantableWorkspaceContextInterface, UmbRoutableWorkspaceContext
|
||||
extends UmbSaveableWorkspaceContextBase<BlockTypeData>
|
||||
implements UmbInvariantDatasetWorkspaceContext, UmbRoutableWorkspaceContext
|
||||
{
|
||||
// Just for context token safety:
|
||||
public readonly IS_BLOCK_TYPE_WORKSPACE_CONTEXT = true;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { UmbBlockWorkspaceContext } from './block-workspace.context.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { UmbWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
|
||||
export const UMB_BLOCK_WORKSPACE_CONTEXT = new UmbContextToken<UmbWorkspaceContextInterface, UmbBlockWorkspaceContext>(
|
||||
export const UMB_BLOCK_WORKSPACE_CONTEXT = new UmbContextToken<UmbWorkspaceContext, UmbBlockWorkspaceContext>(
|
||||
'UmbWorkspaceContext',
|
||||
undefined,
|
||||
(context): context is UmbBlockWorkspaceContext => (context as any).IS_BLOCK_WORKSPACE_CONTEXT,
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { UmbBlockDataType, UmbBlockLayoutBaseModel } from '../types.js';
|
||||
import { UmbBlockElementManager } from './block-element-manager.js';
|
||||
import { UmbBlockWorkspaceEditorElement } from './block-workspace-editor.element.js';
|
||||
import {
|
||||
UmbEditableWorkspaceContextBase,
|
||||
UmbSaveableWorkspaceContextBase,
|
||||
UmbWorkspaceRouteManager,
|
||||
type UmbRoutableWorkspaceContext,
|
||||
UmbWorkspaceIsNewRedirectController,
|
||||
@@ -20,7 +20,7 @@ import { decodeFilePath } from '@umbraco-cms/backoffice/utils';
|
||||
|
||||
export type UmbBlockWorkspaceElementManagerNames = 'content' | 'settings';
|
||||
export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseModel = UmbBlockLayoutBaseModel>
|
||||
extends UmbEditableWorkspaceContextBase<LayoutDataType>
|
||||
extends UmbSaveableWorkspaceContextBase<LayoutDataType>
|
||||
implements UmbRoutableWorkspaceContext
|
||||
{
|
||||
// Just for context token safety:
|
||||
@@ -48,7 +48,7 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
|
||||
|
||||
#layout = new UmbObjectState<LayoutDataType | undefined>(undefined);
|
||||
readonly layout = this.#layout.asObservable();
|
||||
//readonly unique = this.#layout.asObservablePart((x) => x?.contentUdi);
|
||||
readonly unique = this.#layout.asObservablePart((x) => x?.contentUdi);
|
||||
readonly contentUdi = this.#layout.asObservablePart((x) => x?.contentUdi);
|
||||
|
||||
readonly content = new UmbBlockElementManager(this);
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
import { html, nothing, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { map } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import type { UmbEntityAction } from '@umbraco-cms/backoffice/entity-action';
|
||||
import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { html, nothing, customElement, property, state, ifDefined } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { UmbSectionSidebarContext } from '@umbraco-cms/backoffice/section';
|
||||
import { UMB_SECTION_SIDEBAR_CONTEXT } from '@umbraco-cms/backoffice/section';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import type { ManifestEntityActionDefaultKind } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { createExtensionApi } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
@customElement('umb-entity-actions-bundle')
|
||||
export class UmbEntityActionsBundleElement extends UmbLitElement {
|
||||
private _entityType?: string;
|
||||
@property({ type: String, attribute: 'entity-type' })
|
||||
public get entityType() {
|
||||
return this._entityType;
|
||||
}
|
||||
public set entityType(value: string | undefined) {
|
||||
const oldValue = this._entityType;
|
||||
if (oldValue === value) return;
|
||||
|
||||
this._entityType = value;
|
||||
this.#observeEntityActions();
|
||||
this.requestUpdate('entityType', oldValue);
|
||||
}
|
||||
entityType?: string;
|
||||
|
||||
@property({ type: String })
|
||||
unique?: string | null;
|
||||
@@ -28,7 +20,13 @@ export class UmbEntityActionsBundleElement extends UmbLitElement {
|
||||
public label?: string;
|
||||
|
||||
@state()
|
||||
private _hasActions = false;
|
||||
private _numberOfActions = 0;
|
||||
|
||||
@state()
|
||||
private _firstActionManifest?: ManifestEntityActionDefaultKind;
|
||||
|
||||
@state()
|
||||
private _firstActionApi?: UmbEntityAction<unknown>;
|
||||
|
||||
#sectionSidebarContext?: UmbSectionSidebarContext;
|
||||
|
||||
@@ -40,36 +38,58 @@ export class UmbEntityActionsBundleElement extends UmbLitElement {
|
||||
});
|
||||
}
|
||||
|
||||
protected updated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
|
||||
if (_changedProperties.has('entityType') && _changedProperties.has('unique')) {
|
||||
this.#observeEntityActions();
|
||||
}
|
||||
}
|
||||
|
||||
#observeEntityActions() {
|
||||
this.observe(
|
||||
umbExtensionsRegistry
|
||||
.byType('entityAction')
|
||||
.pipe(map((actions) => actions.some((action) => action.forEntityTypes.includes(this.entityType!)))),
|
||||
(hasActions) => {
|
||||
this._hasActions = hasActions;
|
||||
umbExtensionsRegistry.byTypeAndFilter('entityAction', (ext) => ext.forEntityTypes.includes(this.entityType!)),
|
||||
async (actions) => {
|
||||
this._numberOfActions = actions.length;
|
||||
this._firstActionManifest =
|
||||
this._numberOfActions > 0 ? (actions[0] as ManifestEntityActionDefaultKind) : undefined;
|
||||
if (!this._firstActionManifest) return;
|
||||
this._firstActionApi = await createExtensionApi(this, this._firstActionManifest, [
|
||||
{ unique: this.unique, entityType: this.entityType, meta: this._firstActionManifest.meta },
|
||||
]);
|
||||
},
|
||||
'umbEntityActionsObserver',
|
||||
);
|
||||
}
|
||||
|
||||
private _openActions() {
|
||||
#openContextMenu() {
|
||||
if (!this.entityType) throw new Error('Entity type is not defined');
|
||||
if (this.unique === undefined) throw new Error('Unique is not defined');
|
||||
this.#sectionSidebarContext?.toggleContextMenu(this.entityType, this.unique, this.label);
|
||||
}
|
||||
|
||||
async #onFirstActionClick(event: PointerEvent) {
|
||||
event.stopPropagation();
|
||||
await this._firstActionApi?.execute();
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
${this._hasActions
|
||||
? html`
|
||||
<uui-action-bar slot="actions">
|
||||
<uui-button @click=${this._openActions} label="Open actions menu">
|
||||
<uui-symbol-more></uui-symbol-more>
|
||||
</uui-button>
|
||||
</uui-action-bar>
|
||||
`
|
||||
: nothing}
|
||||
`;
|
||||
if (this._numberOfActions === 0) return nothing;
|
||||
return html`<uui-action-bar slot="actions"> ${this.#renderFirstAction()} ${this.#renderMore()} </uui-action-bar>`;
|
||||
}
|
||||
|
||||
#renderMore() {
|
||||
if (this._numberOfActions === 1) return nothing;
|
||||
return html`<uui-button @click=${this.#openContextMenu} label="Open actions menu">
|
||||
<uui-symbol-more></uui-symbol-more>
|
||||
</uui-button>`;
|
||||
}
|
||||
|
||||
#renderFirstAction() {
|
||||
if (!this._firstActionApi) return nothing;
|
||||
return html`<uui-button
|
||||
label=${ifDefined(this._firstActionManifest?.meta.label)}
|
||||
@click=${this.#onFirstActionClick}>
|
||||
<uui-icon name=${ifDefined(this._firstActionManifest?.meta.icon)}></uui-icon>
|
||||
</uui-button>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
|
||||
import { html, customElement, property, state, type PropertyValueMap } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
|
||||
function getNumberOrUndefined(value: string) {
|
||||
@@ -8,7 +8,7 @@ function getNumberOrUndefined(value: string) {
|
||||
}
|
||||
|
||||
@customElement('umb-input-number-range')
|
||||
export class UmbInputNumberRangeElement extends FormControlMixin(UmbLitElement) {
|
||||
export class UmbInputNumberRangeElement extends UmbFormControlMixin(UmbLitElement, undefined) {
|
||||
@property({ type: String, attribute: 'min-label' })
|
||||
minLabel = 'Low value';
|
||||
|
||||
@@ -40,7 +40,8 @@ export class UmbInputNumberRangeElement extends FormControlMixin(UmbLitElement)
|
||||
}
|
||||
|
||||
private updateValue() {
|
||||
const newValue = this._minValue || this._maxValue ? (this._minValue || '') + ',' + (this._maxValue || '') : '';
|
||||
const newValue =
|
||||
this._minValue || this._maxValue ? (this._minValue ?? '') + ',' + (this._maxValue ?? '') : undefined;
|
||||
if (super.value !== newValue) {
|
||||
super.value = newValue;
|
||||
}
|
||||
@@ -48,7 +49,7 @@ export class UmbInputNumberRangeElement extends FormControlMixin(UmbLitElement)
|
||||
|
||||
@property()
|
||||
public set value(valueString: string) {
|
||||
if (valueString !== this._value) {
|
||||
if (valueString !== this.value) {
|
||||
const splittedValue = valueString.split(/[ ,]+/);
|
||||
this.minValue = getNumberOrUndefined(splittedValue[0]);
|
||||
this.maxValue = getNumberOrUndefined(splittedValue[1]);
|
||||
@@ -62,19 +63,27 @@ export class UmbInputNumberRangeElement extends FormControlMixin(UmbLitElement)
|
||||
return this;
|
||||
}
|
||||
|
||||
protected firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
|
||||
super.firstUpdated(_changedProperties);
|
||||
this.shadowRoot
|
||||
?.querySelectorAll('uui-input')
|
||||
.forEach((x) => this.addFormControlElement(x as unknown as HTMLInputElement));
|
||||
}
|
||||
|
||||
private _onMinInput(e: InputEvent) {
|
||||
this.minValue = Number((e.target as HTMLInputElement).value);
|
||||
this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
|
||||
this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
|
||||
}
|
||||
|
||||
private _onMaxInput(e: InputEvent) {
|
||||
this.maxValue = Number((e.target as HTMLInputElement).value);
|
||||
this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
|
||||
this.dispatchEvent(new CustomEvent('change', { bubbles: true }));
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<uui-input
|
||||
type="number"
|
||||
required
|
||||
.value=${this._minValue}
|
||||
@input=${this._onMinInput}
|
||||
label=${this.minLabel}></uui-input>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UmbWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
|
||||
@@ -10,7 +10,7 @@ export const UMB_PROPERTY_TYPE_WORKSPACE_ALIAS = 'Umb.Workspace.PropertyType';
|
||||
*/
|
||||
export class UmbPropertyTypeWorkspaceContext
|
||||
extends UmbContextBase<UmbPropertyTypeWorkspaceContext>
|
||||
implements UmbWorkspaceContextInterface
|
||||
implements UmbWorkspaceContext
|
||||
{
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT);
|
||||
@@ -32,7 +32,7 @@ export class UmbPropertyTypeWorkspaceContext
|
||||
export default UmbPropertyTypeWorkspaceContext;
|
||||
|
||||
export const UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT = new UmbContextToken<
|
||||
UmbWorkspaceContextInterface,
|
||||
UmbWorkspaceContext,
|
||||
UmbPropertyTypeWorkspaceContext
|
||||
>(
|
||||
'UmbWorkspaceContext',
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { UmbContentTypeCompositionModel, UmbContentTypeModel, UmbContentTypeSortModel } from '../types.js';
|
||||
import type { UmbContentTypeStructureManager } from '../structure/index.js';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
|
||||
export interface UmbContentTypeWorkspaceContext<ContentTypeType extends UmbContentTypeModel = UmbContentTypeModel>
|
||||
extends UmbSaveableWorkspaceContextInterface {
|
||||
extends UmbSaveableWorkspaceContext {
|
||||
readonly IS_CONTENT_TYPE_WORKSPACE_CONTEXT: true;
|
||||
|
||||
readonly name: Observable<string | undefined>;
|
||||
|
||||
@@ -3,13 +3,13 @@ import type { ManifestMenuItem } from '@umbraco-cms/backoffice/extension-registr
|
||||
const menuItem: ManifestMenuItem = {
|
||||
type: 'menuItem',
|
||||
alias: 'Umb.MenuItem.Extensions',
|
||||
name: 'Extensions Menu Item',
|
||||
weight: 0,
|
||||
name: 'Extension Insights Menu Item',
|
||||
weight: 200,
|
||||
meta: {
|
||||
label: 'Extensions',
|
||||
label: 'Extension Insights',
|
||||
icon: 'icon-wand',
|
||||
entityType: 'extension-root',
|
||||
menus: ['Umb.Menu.Settings'],
|
||||
menus: ['Umb.Menu.AdvancedSettings'],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -43,7 +43,11 @@ import type { ManifestWorkspace, ManifestWorkspaceRoutableKind } from './workspa
|
||||
import type { ManifestWorkspaceAction, ManifestWorkspaceActionDefaultKind } from './workspace-action.model.js';
|
||||
import type { ManifestWorkspaceActionMenuItem } from './workspace-action-menu-item.model.js';
|
||||
import type { ManifestWorkspaceContext } from './workspace-context.model.js';
|
||||
import type { ManifestWorkspaceFooterApp } from './workspace-footer-app.model.js';
|
||||
import type {
|
||||
ManifestWorkspaceFooterApp,
|
||||
ManifestWorkspaceFooterAppMenuBreadcrumbKind,
|
||||
ManifestWorkspaceFooterAppVariantMenuBreadcrumbKind,
|
||||
} from './workspace-footer-app.model.js';
|
||||
import type {
|
||||
ManifestWorkspaceView,
|
||||
ManifestWorkspaceViewContentTypeDesignEditorKind,
|
||||
@@ -110,6 +114,11 @@ export type ManifestEntityActions =
|
||||
| ManifestEntityActionDeleteFolderKind
|
||||
| ManifestEntityActionTrashKind;
|
||||
|
||||
export type ManifestWorkspaceFooterApps =
|
||||
| ManifestWorkspaceFooterApp
|
||||
| ManifestWorkspaceFooterAppMenuBreadcrumbKind
|
||||
| ManifestWorkspaceFooterAppVariantMenuBreadcrumbKind;
|
||||
|
||||
export type ManifestPropertyActions = ManifestPropertyAction | ManifestPropertyActionDefaultKind;
|
||||
|
||||
export type ManifestWorkspaceActions = ManifestWorkspaceAction | ManifestWorkspaceActionDefaultKind;
|
||||
@@ -158,11 +167,11 @@ export type ManifestTypes =
|
||||
| ManifestTreeItem
|
||||
| ManifestTreeStore
|
||||
| ManifestUserProfileApp
|
||||
| ManifestWorkspaces
|
||||
| ManifestWorkspaceActions
|
||||
| ManifestWorkspaceActionMenuItem
|
||||
| ManifestWorkspaceActions
|
||||
| ManifestWorkspaceContext
|
||||
| ManifestWorkspaceFooterApp
|
||||
| ManifestWorkspaceFooterApps
|
||||
| ManifestWorkspaces
|
||||
| ManifestWorkspaceViews
|
||||
| ManifestEntityUserPermission
|
||||
| ManifestGranularUserPermission
|
||||
|
||||
@@ -7,3 +7,13 @@ export interface ManifestWorkspaceFooterApp
|
||||
ManifestWithDynamicConditions<ConditionTypes> {
|
||||
type: 'workspaceFooterApp';
|
||||
}
|
||||
|
||||
export interface ManifestWorkspaceFooterAppMenuBreadcrumbKind extends ManifestWorkspaceFooterApp {
|
||||
type: 'workspaceFooterApp';
|
||||
kind: 'menuBreadcrumb';
|
||||
}
|
||||
|
||||
export interface ManifestWorkspaceFooterAppVariantMenuBreadcrumbKind extends ManifestWorkspaceFooterApp {
|
||||
type: 'workspaceFooterApp';
|
||||
kind: 'variantMenuBreadcrumb';
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { UmbRoutableWorkspaceContext } from '../../workspace/contexts/routable-workspace-context.interface.js';
|
||||
import type { UmbWorkspaceContextInterface } from '../../workspace/contexts/workspace-context.interface.js';
|
||||
import type { UmbRoutableWorkspaceContext } from '../../workspace/contexts/tokens/routable-workspace-context.interface.js';
|
||||
import type { UmbWorkspaceContext } from '../../workspace/contexts/tokens/workspace-context.interface.js';
|
||||
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { ManifestElementAndApi } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { ManifestElementAndApi } from '@umbraco-cms/backoffice/extension-ap
|
||||
export interface ManifestWorkspace<
|
||||
MetaType extends MetaWorkspace = MetaWorkspace,
|
||||
ElementType extends UmbControllerHostElement = UmbControllerHostElement,
|
||||
ApiType extends UmbWorkspaceContextInterface = UmbWorkspaceContextInterface,
|
||||
ApiType extends UmbWorkspaceContext = UmbWorkspaceContext,
|
||||
> extends ManifestElementAndApi<ElementType, ApiType> {
|
||||
type: 'workspace';
|
||||
meta: MetaType;
|
||||
|
||||
@@ -27,6 +27,12 @@ export interface UmbracoPackage {
|
||||
*/
|
||||
allowTelemetry?: boolean;
|
||||
|
||||
/**
|
||||
* @title Decides if the package is allowed to be accessed by the public, e.g. on the login screen
|
||||
* @default false
|
||||
*/
|
||||
allowPublicAccess?: boolean;
|
||||
|
||||
/**
|
||||
* @title An array of Umbraco package manifest types that will be installed
|
||||
* @required
|
||||
|
||||
@@ -7,7 +7,10 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
export class UmbExtensionRootWorkspaceElement extends UmbLitElement {
|
||||
render() {
|
||||
return html`
|
||||
<umb-workspace-editor headline="Extensions" alias=${UMB_EXTENSION_ROOT_WORKSPACE_ALIAS} .enforceNoFooter=${true}>
|
||||
<umb-workspace-editor
|
||||
headline="Extension Insights"
|
||||
alias=${UMB_EXTENSION_ROOT_WORKSPACE_ALIAS}
|
||||
.enforceNoFooter=${true}>
|
||||
<umb-collection alias=${UMB_EXTENSION_COLLECTION_ALIAS}></umb-collection>
|
||||
</umb-workspace-editor>
|
||||
`;
|
||||
|
||||
@@ -13,20 +13,20 @@ export class UmbLocalizeDateElement extends UmbLitElement {
|
||||
* @attr
|
||||
* @example date="Sep 22 2023"
|
||||
*/
|
||||
@property()
|
||||
date!: string | Date;
|
||||
@property({ type: String })
|
||||
date?: string | Date;
|
||||
|
||||
/**
|
||||
* Formatting options
|
||||
* @attr
|
||||
* @example options={ dateStyle: 'full', timeStyle: 'long', timeZone: 'Australia/Sydney' }
|
||||
*/
|
||||
@property()
|
||||
@property({ type: Object })
|
||||
options?: Intl.DateTimeFormatOptions;
|
||||
|
||||
@state()
|
||||
protected get text(): string {
|
||||
return this.localize.date(this.date, this.options);
|
||||
return this.localize.date(this.date!, this.options);
|
||||
}
|
||||
|
||||
protected render() {
|
||||
|
||||
@@ -2,3 +2,8 @@ export * from './menu-item/index.js';
|
||||
export * from './menu-item-layout/index.js';
|
||||
export * from './menu.element.js';
|
||||
export * from './menu.context.js';
|
||||
export * from './menu-tree-structure-workspace-context-base.js';
|
||||
export * from './menu-variant-tree-structure-workspace-context-base.js';
|
||||
export * from './types.js';
|
||||
|
||||
export type { UmbMenuStructureWorkspaceContext } from './menu-structure-workspace-context.interface.js';
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { UmbStructureItemModel } from './types.js';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
|
||||
export interface UmbMenuStructureWorkspaceContext {
|
||||
structure: Observable<UmbStructureItemModel[]>;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import type { UmbStructureItemModel } from './types.js';
|
||||
import type { UmbTreeRepository, UmbUniqueTreeItemModel, UmbUniqueTreeRootModel } from '@umbraco-cms/backoffice/tree';
|
||||
import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
interface UmbMenuTreeStructureWorkspaceContextBaseArgs {
|
||||
treeRepositoryAlias: string;
|
||||
}
|
||||
|
||||
export abstract class UmbMenuTreeStructureWorkspaceContextBase extends UmbContextBase<unknown> {
|
||||
#workspaceContext?: any;
|
||||
#args: UmbMenuTreeStructureWorkspaceContextBaseArgs;
|
||||
|
||||
#structure = new UmbArrayState<UmbStructureItemModel>([], (x) => x.unique);
|
||||
public readonly structure = this.#structure.asObservable();
|
||||
|
||||
constructor(host: UmbControllerHost, args: UmbMenuTreeStructureWorkspaceContextBaseArgs) {
|
||||
// TODO: set up context token
|
||||
super(host, 'UmbMenuStructureWorkspaceContext');
|
||||
this.#args = args;
|
||||
|
||||
this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => {
|
||||
this.#workspaceContext = instance;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
this.#workspaceContext.observe(this.#workspaceContext.unique, (value) => {
|
||||
if (!value) return;
|
||||
this.#requestStructure();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async #requestStructure() {
|
||||
let structureItems: Array<UmbStructureItemModel> = [];
|
||||
|
||||
const treeRepository = await createExtensionApiByAlias<
|
||||
UmbTreeRepository<UmbUniqueTreeItemModel, UmbUniqueTreeRootModel>
|
||||
>(this, this.#args.treeRepositoryAlias);
|
||||
|
||||
const { data: root } = await treeRepository.requestTreeRoot();
|
||||
|
||||
if (root) {
|
||||
structureItems = [
|
||||
{
|
||||
unique: root.unique,
|
||||
entityType: root.entityType,
|
||||
name: root.name,
|
||||
isFolder: root.isFolder,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const isNew = this.#workspaceContext?.getIsNew();
|
||||
const uniqueObservable = isNew ? this.#workspaceContext?.parentUnique : this.#workspaceContext?.unique;
|
||||
|
||||
const unique = (await this.observe(uniqueObservable, () => {})?.asPromise()) as string;
|
||||
if (!unique) throw new Error('Unique is not available');
|
||||
|
||||
const { data } = await treeRepository.requestTreeItemAncestors({ descendantUnique: unique });
|
||||
|
||||
if (data) {
|
||||
const ancestorItems = data.map((treeItem) => {
|
||||
return {
|
||||
unique: treeItem.unique,
|
||||
entityType: treeItem.entityType,
|
||||
name: treeItem.name,
|
||||
isFolder: treeItem.isFolder,
|
||||
};
|
||||
});
|
||||
structureItems.push(...ancestorItems);
|
||||
}
|
||||
|
||||
this.#structure.setValue(structureItems);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import type { UmbVariantStructureItemModel } from './types.js';
|
||||
import type { UmbTreeRepository } from '@umbraco-cms/backoffice/tree';
|
||||
import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import { UMB_VARIANT_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
interface UmbMenuVariantTreeStructureWorkspaceContextBaseArgs {
|
||||
treeRepositoryAlias: string;
|
||||
}
|
||||
|
||||
export abstract class UmbMenuVariantTreeStructureWorkspaceContextBase extends UmbContextBase<unknown> {
|
||||
// TODO: add correct interface
|
||||
#workspaceContext?: any;
|
||||
#args: UmbMenuVariantTreeStructureWorkspaceContextBaseArgs;
|
||||
|
||||
#structure = new UmbArrayState<UmbVariantStructureItemModel>([], (x) => x.unique);
|
||||
public readonly structure = this.#structure.asObservable();
|
||||
|
||||
constructor(host: UmbControllerHost, args: UmbMenuVariantTreeStructureWorkspaceContextBaseArgs) {
|
||||
// TODO: set up context token
|
||||
super(host, 'UmbMenuStructureWorkspaceContext');
|
||||
this.#args = args;
|
||||
|
||||
this.consumeContext(UMB_VARIANT_WORKSPACE_CONTEXT, (instance) => {
|
||||
this.#workspaceContext = instance;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
this.#workspaceContext.observe(this.#workspaceContext.unique, (value) => {
|
||||
if (!value) return;
|
||||
this.#requestStructure();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async #requestStructure() {
|
||||
const isNew = this.#workspaceContext?.getIsNew();
|
||||
const uniqueObservable = isNew ? this.#workspaceContext?.parentUnique : this.#workspaceContext?.unique;
|
||||
|
||||
const unique = (await this.observe(uniqueObservable, () => {})?.asPromise()) as string;
|
||||
if (!unique) throw new Error('Unique is not available');
|
||||
|
||||
const treeRepository = await createExtensionApiByAlias<UmbTreeRepository<any>>(
|
||||
this,
|
||||
this.#args.treeRepositoryAlias,
|
||||
);
|
||||
const { data } = await treeRepository.requestTreeItemAncestors({ descendantUnique: unique });
|
||||
|
||||
if (data) {
|
||||
const structureItems = data.map((treeItem) => {
|
||||
return {
|
||||
unique: treeItem.unique,
|
||||
entityType: treeItem.entityType,
|
||||
variants: treeItem.variants.map((variant: any) => {
|
||||
return {
|
||||
name: variant.name,
|
||||
culture: variant.culture,
|
||||
segment: variant.segment,
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
this.#structure.setValue(structureItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/Umbraco.Web.UI.Client/src/packages/core/menu/types.ts
Normal file
13
src/Umbraco.Web.UI.Client/src/packages/core/menu/types.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export interface UmbStructureItemModelBase {
|
||||
unique: string | null;
|
||||
entityType: string;
|
||||
}
|
||||
|
||||
export interface UmbStructureItemModel extends UmbStructureItemModelBase {
|
||||
name: string;
|
||||
isFolder: boolean;
|
||||
}
|
||||
|
||||
export interface UmbVariantStructureItemModel extends UmbStructureItemModelBase {
|
||||
variants: Array<{ name: string; culture: string | null; segment: string | null }>;
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
export type UmbEntityUnique = string | null;
|
||||
|
||||
/** Tried to find a common base of our entities — used by Entity Workspace Context */
|
||||
export type UmbEntityBase = {
|
||||
id?: string;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { html, customElement, property, state } from '@umbraco-cms/backoffice/ex
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UMB_DOCUMENT_COLLECTION_ALIAS } from '@umbraco-cms/backoffice/document';
|
||||
import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property';
|
||||
import { UMB_WORKSPACE_COLLECTION_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import { UMB_COLLECTION_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type {
|
||||
UmbCollectionBulkActionPermissions,
|
||||
@@ -31,7 +31,7 @@ export class UmbPropertyEditorUICollectionViewElement extends UmbLitElement impl
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_WORKSPACE_COLLECTION_CONTEXT, (workspaceContext) => {
|
||||
this.consumeContext(UMB_COLLECTION_WORKSPACE_CONTEXT, (workspaceContext) => {
|
||||
this._collectionAlias = workspaceContext.getCollectionAlias();
|
||||
|
||||
this.consumeContext(UMB_PROPERTY_CONTEXT, (propertyContext) => {
|
||||
@@ -39,8 +39,9 @@ export class UmbPropertyEditorUICollectionViewElement extends UmbLitElement impl
|
||||
if (propertyAlias) {
|
||||
// Gets the Data Type ID for the current property.
|
||||
const property = await workspaceContext.structure.getPropertyStructureByAlias(propertyAlias);
|
||||
if (property && this._config) {
|
||||
this._config.unique = workspaceContext.getUnique();
|
||||
const unique = workspaceContext.getUnique();
|
||||
if (unique && property && this._config) {
|
||||
this._config.unique = unique;
|
||||
this._config.dataTypeId = property.dataType.unique;
|
||||
this.requestUpdate('_config');
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { html, customElement, property, state } from '@umbraco-cms/backoffice/ex
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor';
|
||||
import { UmbDynamicRootRepository } from '@umbraco-cms/backoffice/dynamic-root';
|
||||
import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import { UMB_ENTITY_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type { UmbInputTreeElement } from '@umbraco-cms/backoffice/tree';
|
||||
@@ -69,7 +69,7 @@ export class UmbPropertyEditorUITreePickerElement extends UmbLitElement implemen
|
||||
|
||||
// TODO: Awaiting the workspace context to have a parent entity ID value. [LK]
|
||||
// e.g. const parentEntityId = this.#workspaceContext?.getParentEntityId();
|
||||
const workspaceContext = await this.getContext(UMB_WORKSPACE_CONTEXT);
|
||||
const workspaceContext = await this.getContext(UMB_ENTITY_WORKSPACE_CONTEXT);
|
||||
const unique = workspaceContext.getUnique();
|
||||
if (unique && this.#dynamicRoot) {
|
||||
const result = await this.#dynamicRootRepository.postDynamicRootQuery(this.#dynamicRoot, unique);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { UmbVariantId } from '../../variant/variant-id.class.js';
|
||||
import type { UmbContext } from '@umbraco-cms/backoffice/class-api';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import type { UmbEntityUnique } from '@umbraco-cms/backoffice/models';
|
||||
|
||||
/**
|
||||
* A property dataset context, represents the data of a set of properties.
|
||||
@@ -18,7 +19,7 @@ import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
*/
|
||||
export interface UmbPropertyDatasetContext extends UmbContext {
|
||||
getEntityType(): string;
|
||||
getUnique(): string | undefined;
|
||||
getUnique(): UmbEntityUnique | undefined;
|
||||
getVariantId: () => UmbVariantId;
|
||||
|
||||
getName(): string | undefined;
|
||||
|
||||
@@ -19,6 +19,10 @@ export class UmbSectionContext {
|
||||
this.#manifestPathname.setValue(manifest?.meta?.pathname);
|
||||
this.#manifestLabel.setValue(manifest ? manifest.meta?.label || manifest.name : undefined);
|
||||
}
|
||||
|
||||
getPathname() {
|
||||
return this.#manifestPathname.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
export const UMB_SECTION_CONTEXT = new UmbContextToken<UmbSectionContext>('UmbSectionContext');
|
||||
|
||||
@@ -21,21 +21,40 @@ export const manifests = [
|
||||
},
|
||||
{
|
||||
type: 'menu',
|
||||
alias: 'Umb.Menu.Settings',
|
||||
alias: 'Umb.Menu.StructureSettings',
|
||||
name: 'Settings Menu',
|
||||
meta: {
|
||||
label: 'Settings',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'sectionSidebarApp',
|
||||
kind: 'menu',
|
||||
alias: 'Umb.SectionSidebarMenu.Settings',
|
||||
name: 'Settings Section Sidebar Menu',
|
||||
weight: 200,
|
||||
name: 'Structure Settings Sidebar Menu',
|
||||
weight: 300,
|
||||
meta: {
|
||||
label: 'Settings',
|
||||
menu: 'Umb.Menu.Settings',
|
||||
label: 'Structure',
|
||||
menu: 'Umb.Menu.StructureSettings',
|
||||
},
|
||||
conditions: [
|
||||
{
|
||||
alias: 'Umb.Condition.SectionAlias',
|
||||
match: UMB_SETTINGS_SECTION_ALIAS,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'menu',
|
||||
alias: 'Umb.Menu.AdvancedSettings',
|
||||
name: 'Advanced Settings Menu',
|
||||
},
|
||||
{
|
||||
type: 'sectionSidebarApp',
|
||||
kind: 'menu',
|
||||
alias: 'Umb.SectionSidebarMenu.AdvancedSettings',
|
||||
name: 'Advanced Settings Sidebar Menu',
|
||||
weight: 100,
|
||||
meta: {
|
||||
label: 'Advanced',
|
||||
menu: 'Umb.Menu.AdvancedSettings',
|
||||
},
|
||||
conditions: [
|
||||
{
|
||||
|
||||
@@ -5,6 +5,10 @@ export type { UmbTreeDataSource } from './tree-data-source.interface.js';
|
||||
export type { UmbTreeRepository } from './tree-repository.interface.js';
|
||||
export type { UmbTreeStore } from './tree-store.interface.js';
|
||||
|
||||
export type { UmbTreeRootItemsRequestArgs, UmbTreeChildrenOfRequestArgs } from './types.js';
|
||||
export type {
|
||||
UmbTreeRootItemsRequestArgs,
|
||||
UmbTreeChildrenOfRequestArgs,
|
||||
UmbTreeAncestorsOfRequestArgs,
|
||||
} from './types.js';
|
||||
|
||||
export { UmbUniqueTreeStore } from './unique-tree-store.js';
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import type { UmbTreeItemModelBase } from '../types.js';
|
||||
import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from './types.js';
|
||||
import type {
|
||||
UmbTreeAncestorsOfRequestArgs,
|
||||
UmbTreeChildrenOfRequestArgs,
|
||||
UmbTreeRootItemsRequestArgs,
|
||||
} from './types.js';
|
||||
import type { UmbPagedModel, UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
@@ -33,4 +37,11 @@ export interface UmbTreeDataSource<TreeItemType extends UmbTreeItemModelBase> {
|
||||
* @memberof UmbTreeDataSource
|
||||
*/
|
||||
getChildrenOf(args: UmbTreeChildrenOfRequestArgs): Promise<UmbDataSourceResponse<UmbPagedModel<TreeItemType>>>;
|
||||
|
||||
/**
|
||||
* Gets the ancestors of the given item.
|
||||
* @return {*} {Promise<UmbDataSourceResponse<Array<TreeItemType>>}
|
||||
* @memberof UmbTreeDataSource
|
||||
*/
|
||||
getAncestorsOf(args: UmbTreeAncestorsOfRequestArgs): Promise<UmbDataSourceResponse<Array<TreeItemType>>>;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { UmbUniqueTreeItemModel, UmbUniqueTreeRootModel } from '../types.js
|
||||
import type { UmbTreeStore } from './tree-store.interface.js';
|
||||
import type { UmbTreeRepository } from './tree-repository.interface.js';
|
||||
import type { UmbTreeDataSource, UmbTreeDataSourceConstructor } from './tree-data-source.interface.js';
|
||||
import type { UmbTreeAncestorsOfRequestArgs } from './types.js';
|
||||
import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { UmbApi } from '@umbraco-cms/backoffice/extension-api';
|
||||
@@ -92,6 +93,22 @@ export abstract class UmbTreeRepositoryBase<
|
||||
return { data, error, asObservable: () => this._treeStore!.childrenOf(args.parentUnique) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests ancestors of a given item
|
||||
* @param {UmbTreeAncestorsOfRequestArgs} args
|
||||
* @return {*}
|
||||
* @memberof UmbTreeRepositoryBase
|
||||
*/
|
||||
async requestTreeItemAncestors(args: UmbTreeAncestorsOfRequestArgs) {
|
||||
if (args.descendantUnique === undefined) throw new Error('Descendant unique is missing');
|
||||
await this._init;
|
||||
|
||||
const { data, error } = await this.#treeSource.getAncestorsOf(args);
|
||||
|
||||
// TODO: implement observable for ancestor items in the store
|
||||
return { data, error };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise with an observable of tree root items
|
||||
* @return {*}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import type { UmbTreeItemModelBase } from '../types.js';
|
||||
import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from './types.js';
|
||||
import type {
|
||||
UmbTreeChildrenOfRequestArgs,
|
||||
UmbTreeAncestorsOfRequestArgs,
|
||||
UmbTreeRootItemsRequestArgs,
|
||||
} from './types.js';
|
||||
import type { UmbPagedModel } from '@umbraco-cms/backoffice/repository';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import type { ProblemDetails } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
@@ -48,6 +52,15 @@ export interface UmbTreeRepository<
|
||||
asObservable?: () => Observable<TreeItemType[]>;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Requests the ancestors of the given item.
|
||||
* @param {UmbTreeAncestorsOfRequestArgs} args
|
||||
* @memberof UmbTreeRepository
|
||||
*/
|
||||
requestTreeItemAncestors: (
|
||||
args: UmbTreeAncestorsOfRequestArgs,
|
||||
) => Promise<{ data?: TreeItemType[]; error?: ProblemDetails; asObservable?: () => Observable<TreeItemType[]> }>;
|
||||
|
||||
/**
|
||||
* Returns an observable of the root items of the tree.
|
||||
* @memberof UmbTreeRepository
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { UmbTreeItemModelBase } from '../types.js';
|
||||
import type { UmbTreeDataSource } from './tree-data-source.interface.js';
|
||||
import type { UmbTreeChildrenOfRequestArgs, UmbTreeRootItemsRequestArgs } from './types.js';
|
||||
import type {
|
||||
UmbTreeAncestorsOfRequestArgs,
|
||||
UmbTreeChildrenOfRequestArgs,
|
||||
UmbTreeRootItemsRequestArgs,
|
||||
} from './types.js';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { TreeItemPresentationModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
@@ -12,6 +16,7 @@ export interface UmbTreeServerDataSourceBaseArgs<
|
||||
> {
|
||||
getRootItems: (args: UmbTreeRootItemsRequestArgs) => Promise<UmbPagedModel<ServerTreeItemType>>;
|
||||
getChildrenOf: (args: UmbTreeChildrenOfRequestArgs) => Promise<UmbPagedModel<ServerTreeItemType>>;
|
||||
getAncestorsOf: (args: UmbTreeAncestorsOfRequestArgs) => Promise<Array<ServerTreeItemType>>;
|
||||
mapper: (item: ServerTreeItemType) => ClientTreeItemType;
|
||||
}
|
||||
|
||||
@@ -29,6 +34,7 @@ export abstract class UmbTreeServerDataSourceBase<
|
||||
#host;
|
||||
#getRootItems;
|
||||
#getChildrenOf;
|
||||
#getAncestorsOf;
|
||||
#mapper;
|
||||
|
||||
/**
|
||||
@@ -40,6 +46,7 @@ export abstract class UmbTreeServerDataSourceBase<
|
||||
this.#host = host;
|
||||
this.#getRootItems = args.getRootItems;
|
||||
this.#getChildrenOf = args.getChildrenOf;
|
||||
this.#getAncestorsOf = args.getAncestorsOf;
|
||||
this.#mapper = args.mapper;
|
||||
}
|
||||
|
||||
@@ -78,4 +85,23 @@ export abstract class UmbTreeServerDataSourceBase<
|
||||
|
||||
return { error };
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the ancestors of a given item from the server
|
||||
* @param {UmbTreeAncestorsOfRequestArgs} args
|
||||
* @return {*}
|
||||
* @memberof UmbTreeServerDataSourceBase
|
||||
*/
|
||||
async getAncestorsOf(args: UmbTreeAncestorsOfRequestArgs) {
|
||||
if (!args.descendantUnique) throw new Error('Parent unique is missing');
|
||||
|
||||
const { data, error } = await tryExecuteAndNotify(this.#host, this.#getAncestorsOf(args));
|
||||
|
||||
if (data) {
|
||||
const items = data?.map((item: ServerTreeItemType) => this.#mapper(item));
|
||||
return { data: items };
|
||||
}
|
||||
|
||||
return { error };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,3 +8,7 @@ export interface UmbTreeChildrenOfRequestArgs {
|
||||
skip: number;
|
||||
take: number;
|
||||
}
|
||||
|
||||
export interface UmbTreeAncestorsOfRequestArgs {
|
||||
descendantUnique: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './validation.context.js';
|
||||
export * from './validation.context-token.js';
|
||||
@@ -0,0 +1,4 @@
|
||||
import type { UmbValidationContext } from './validation.context.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
|
||||
export const UMB_VALIDATION_CONTEXT = new UmbContextToken<UmbValidationContext>('UmbValidationContext');
|
||||
@@ -0,0 +1,9 @@
|
||||
import { UMB_VALIDATION_CONTEXT } from './validation.context-token.js';
|
||||
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export class UmbValidationContext extends UmbContextBase<UmbValidationContext> {
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UMB_VALIDATION_CONTEXT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './validation-valid.event.js';
|
||||
export * from './validation-invalid.event.js';
|
||||
@@ -0,0 +1,9 @@
|
||||
import { UmbValidationEvent } from './validation.event.js';
|
||||
|
||||
export class UmbValidationInvalidEvent extends UmbValidationEvent {
|
||||
static readonly TYPE = 'invalid';
|
||||
|
||||
public constructor() {
|
||||
super(UmbValidationInvalidEvent.TYPE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { UmbValidationEvent } from './validation.event.js';
|
||||
|
||||
export class UmbValidationValidEvent extends UmbValidationEvent {
|
||||
static readonly TYPE = 'valid';
|
||||
|
||||
constructor() {
|
||||
super(UmbValidationValidEvent.TYPE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export class UmbValidationEvent extends Event {
|
||||
public constructor(type: string) {
|
||||
super(type, { bubbles: true, composed: true, cancelable: false });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './events/index.js';
|
||||
export * from './mixins/index.js';
|
||||
export * from './context/index.js';
|
||||
@@ -0,0 +1,306 @@
|
||||
import { UmbValidationInvalidEvent } from '../events/validation-invalid.event.js';
|
||||
import { UmbValidationValidEvent } from '../events/validation-valid.event.js';
|
||||
import { UmbValidityState } from './validity-state.class.js';
|
||||
import { property, type LitElement } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { HTMLElementConstructor } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
type NativeFormControlElement = HTMLInputElement | HTMLTextAreaElement; // Eventually use a specific interface or list multiple options like appending these types: ... | HTMLTextAreaElement | HTMLSelectElement
|
||||
|
||||
/* FlagTypes type options originate from:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/ValidityState
|
||||
* */
|
||||
type FlagTypes =
|
||||
| 'badInput'
|
||||
| 'customError'
|
||||
| 'patternMismatch'
|
||||
| 'rangeOverflow'
|
||||
| 'rangeUnderflow'
|
||||
| 'stepMismatch'
|
||||
| 'tooLong'
|
||||
| 'tooShort'
|
||||
| 'typeMismatch'
|
||||
| 'valueMissing'
|
||||
| 'badInput'
|
||||
| 'valid';
|
||||
|
||||
// Acceptable as an internal interface/type, BUT if exposed externally this should be turned into a public class in a separate file.
|
||||
interface Validator {
|
||||
flagKey: FlagTypes;
|
||||
getMessageMethod: () => string;
|
||||
checkMethod: () => boolean;
|
||||
}
|
||||
|
||||
export interface UmbFormControlMixinInterface<ValueType, DefaultValueType> extends HTMLElement {
|
||||
formAssociated: boolean;
|
||||
get value(): ValueType | DefaultValueType;
|
||||
set value(newValue: ValueType | DefaultValueType);
|
||||
formResetCallback(): void;
|
||||
checkValidity(): boolean;
|
||||
get validationMessage(): string;
|
||||
get validity(): ValidityState;
|
||||
setCustomValidity(error: string): void;
|
||||
submit(): void;
|
||||
pristine: boolean;
|
||||
required: boolean;
|
||||
requiredMessage: string;
|
||||
error: boolean;
|
||||
errorMessage: string;
|
||||
}
|
||||
|
||||
export declare abstract class UmbFormControlMixinElement<ValueType, DefaultValueType>
|
||||
extends LitElement
|
||||
implements UmbFormControlMixinInterface<ValueType, DefaultValueType>
|
||||
{
|
||||
protected _internals: ElementInternals;
|
||||
protected _runValidators(): void;
|
||||
protected abstract getFormElement(): HTMLElement | undefined;
|
||||
protected addValidator: (flagKey: FlagTypes, getMessageMethod: () => string, checkMethod: () => boolean) => void;
|
||||
protected addFormControlElement(element: NativeFormControlElement): void;
|
||||
|
||||
formAssociated: boolean;
|
||||
get value(): ValueType | DefaultValueType;
|
||||
set value(newValue: ValueType | DefaultValueType);
|
||||
formResetCallback(): void;
|
||||
checkValidity(): boolean;
|
||||
get validationMessage(): string;
|
||||
get validity(): ValidityState;
|
||||
setCustomValidity(error: string): void;
|
||||
submit(): void;
|
||||
pristine: boolean;
|
||||
required: boolean;
|
||||
requiredMessage: string;
|
||||
error: boolean;
|
||||
errorMessage: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The mixin allows a custom element to participate in HTML forms.
|
||||
*
|
||||
* @param {Object} superClass - superclass to be extended.
|
||||
* @mixin
|
||||
*/
|
||||
export const UmbFormControlMixin = <
|
||||
ValueType = FormDataEntryValue | FormData,
|
||||
T extends HTMLElementConstructor<LitElement> = HTMLElementConstructor<LitElement>,
|
||||
DefaultValueType = unknown,
|
||||
>(
|
||||
superClass: T,
|
||||
defaultValue: DefaultValueType,
|
||||
) => {
|
||||
abstract class UmbFormControlMixinClass extends superClass {
|
||||
/**
|
||||
* This is a static class field indicating that the element is can be used inside a native form and participate in its events.
|
||||
* It may require a polyfill, check support here https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals.
|
||||
* Read more about form controls here https://web.dev/more-capable-form-controls/
|
||||
* @type {boolean}
|
||||
*/
|
||||
static readonly formAssociated = true;
|
||||
|
||||
/**
|
||||
* Value of this form control.
|
||||
* If you dont want the setFormValue to be called on the ElementInternals, then prevent calling this method, by not calling super.value = newValue in your implementation of the value setter method.
|
||||
* @type {string}
|
||||
* @attr value
|
||||
* @default ''
|
||||
*/
|
||||
@property({ reflect: false }) // Do not 'reflect' as the attribute is used as fallback.
|
||||
get value(): ValueType | DefaultValueType {
|
||||
return this.#value;
|
||||
}
|
||||
set value(newValue: ValueType | DefaultValueType) {
|
||||
this.#value = newValue;
|
||||
this._runValidators();
|
||||
}
|
||||
|
||||
// Validation
|
||||
private _validityState = new UmbValidityState();
|
||||
|
||||
/**
|
||||
* Determines wether the form control has been touched or interacted with, this determines wether the validation-status of this form control should be made visible.
|
||||
* @type {boolean}
|
||||
* @attr
|
||||
* @default false
|
||||
*/
|
||||
@property({ type: Boolean, reflect: true })
|
||||
pristine: boolean = true;
|
||||
|
||||
#value: ValueType | DefaultValueType = defaultValue;
|
||||
protected _internals: ElementInternals;
|
||||
#form: HTMLFormElement | null = null;
|
||||
#validators: Validator[] = [];
|
||||
#formCtrlElements: NativeFormControlElement[] = [];
|
||||
|
||||
constructor(...args: any[]) {
|
||||
super(...args);
|
||||
this._internals = this.attachInternals();
|
||||
|
||||
this.addEventListener('blur', () => {
|
||||
this.pristine = false;
|
||||
this.checkValidity();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get internal form element.
|
||||
* This has to be implemented to provide a FormControl Element of choice for the given context. The element is used as anchor for validation-messages.
|
||||
* @abstract
|
||||
* @method getFormElement
|
||||
* @returns {HTMLElement | undefined}
|
||||
*/
|
||||
protected abstract getFormElement(): HTMLElement | undefined;
|
||||
|
||||
disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this.#removeFormListeners();
|
||||
}
|
||||
#removeFormListeners() {
|
||||
if (this.#form) {
|
||||
this.#form.removeEventListener('submit', this.#onFormSubmit);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add validator, to validate this Form Control.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/API/ValidityState for available Validator FlagTypes.
|
||||
*
|
||||
* @example
|
||||
* this.addValidator(
|
||||
* 'tooLong',
|
||||
* () => 'This input contains too many characters',
|
||||
* () => this._value.length > 10
|
||||
* );
|
||||
* @method addValidator
|
||||
* @param {FlagTypes} flagKey the type of validation.
|
||||
* @param {method} getMessageMethod method to retrieve relevant message. Is executed every time the validator is re-executed.
|
||||
* @param {method} checkMethod method to determine if this validator should invalidate this form control. Return true if this should prevent submission.
|
||||
*/
|
||||
protected addValidator(flagKey: FlagTypes, getMessageMethod: () => string, checkMethod: () => boolean): Validator {
|
||||
const obj = {
|
||||
flagKey: flagKey,
|
||||
getMessageMethod: getMessageMethod,
|
||||
checkMethod: checkMethod,
|
||||
};
|
||||
this.#validators.push(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
protected removeValidator(validator: Validator) {
|
||||
const index = this.#validators.indexOf(validator);
|
||||
if (index !== -1) {
|
||||
this.#validators.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @method addFormControlElement
|
||||
* @description Important notice if adding a native form control then ensure that its value and thereby validity is updated when value is changed from the outside.
|
||||
* @param element {NativeFormControlElement} - element to validate and include as part of this form association.
|
||||
*/
|
||||
protected addFormControlElement(element: NativeFormControlElement) {
|
||||
this.#formCtrlElements.push(element);
|
||||
element.addEventListener(UmbValidationInvalidEvent.TYPE, () => {
|
||||
this._runValidators();
|
||||
});
|
||||
element.addEventListener(UmbValidationValidEvent.TYPE, () => {
|
||||
this._runValidators();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @method _runValidators
|
||||
* @description Run all validators and set the validityState of this form control.
|
||||
* Run this method when you want to re-run all validators.
|
||||
* This can be relevant if you have a validators that is using values that is not triggering the Lit Updated Callback.
|
||||
* Such are mainly properties that are not declared as a Lit state and or Lit property.
|
||||
*/
|
||||
protected _runValidators() {
|
||||
this._validityState = new UmbValidityState();
|
||||
|
||||
// Loop through inner native form controls to adapt their validityState.
|
||||
this.#formCtrlElements.forEach((formCtrlEl) => {
|
||||
let key: keyof ValidityState;
|
||||
for (key in formCtrlEl.validity) {
|
||||
if (key !== 'valid' && formCtrlEl.validity[key]) {
|
||||
this._validityState[key] = true;
|
||||
this._internals.setValidity(this._validityState, formCtrlEl.validationMessage, formCtrlEl);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Loop through custom validators, currently its intentional to have them overwritten native validity. but might need to be reconsidered (This current way enables to overwrite with custom messages) [NL]
|
||||
this.#validators.forEach((validator) => {
|
||||
if (validator.checkMethod()) {
|
||||
this._validityState[validator.flagKey] = true;
|
||||
this._internals.setValidity(this._validityState, validator.getMessageMethod(), this.getFormElement());
|
||||
}
|
||||
});
|
||||
|
||||
const hasError = Object.values(this._validityState).includes(true);
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/ValidityState#valid
|
||||
this._validityState.valid = !hasError;
|
||||
|
||||
if (hasError) {
|
||||
this.dispatchEvent(new UmbValidationInvalidEvent());
|
||||
} else {
|
||||
this._internals.setValidity({});
|
||||
this.dispatchEvent(new UmbValidationValidEvent());
|
||||
}
|
||||
}
|
||||
|
||||
updated(changedProperties: Map<string | number | symbol, unknown>) {
|
||||
super.updated(changedProperties);
|
||||
this._runValidators();
|
||||
}
|
||||
|
||||
#onFormSubmit = () => {
|
||||
this.pristine = false;
|
||||
};
|
||||
|
||||
public formAssociatedCallback() {
|
||||
this.#removeFormListeners();
|
||||
this.#form = this._internals.form;
|
||||
if (this.#form) {
|
||||
// This relies on the form begin a 'uui-form': [NL]
|
||||
if (this.#form.hasAttribute('submit-invalid')) {
|
||||
this.pristine = false;
|
||||
}
|
||||
this.#form.addEventListener('submit', this.#onFormSubmit);
|
||||
}
|
||||
}
|
||||
public formResetCallback() {
|
||||
this.pristine = true;
|
||||
this.value = this.getInitialValue() ?? this.getDefaultValue();
|
||||
}
|
||||
|
||||
protected getDefaultValue(): DefaultValueType {
|
||||
return defaultValue;
|
||||
}
|
||||
protected getInitialValue(): ValueType | DefaultValueType {
|
||||
return this.getAttribute('value') as ValueType | DefaultValueType;
|
||||
}
|
||||
|
||||
public checkValidity() {
|
||||
for (const key in this.#formCtrlElements) {
|
||||
if (this.#formCtrlElements[key].checkValidity() === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return this._internals?.checkValidity();
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/validity
|
||||
public get validity(): ValidityState {
|
||||
return this._validityState;
|
||||
}
|
||||
|
||||
get validationMessage() {
|
||||
return this._internals?.validationMessage;
|
||||
}
|
||||
}
|
||||
return UmbFormControlMixinClass as unknown as HTMLElementConstructor<
|
||||
UmbFormControlMixinElement<ValueType, DefaultValueType>
|
||||
> &
|
||||
T;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './form-control.mixin.js';
|
||||
@@ -0,0 +1,15 @@
|
||||
type Writeable<T> = { -readonly [P in keyof T]: T[P] };
|
||||
|
||||
export class UmbValidityState implements Writeable<ValidityState> {
|
||||
badInput: boolean = false;
|
||||
customError: boolean = false;
|
||||
patternMismatch: boolean = false;
|
||||
rangeOverflow: boolean = false;
|
||||
rangeUnderflow: boolean = false;
|
||||
stepMismatch: boolean = false;
|
||||
tooLong: boolean = false;
|
||||
tooShort: boolean = false;
|
||||
typeMismatch: boolean = false;
|
||||
valid: boolean = false;
|
||||
valueMissing: boolean = false;
|
||||
}
|
||||
@@ -1,4 +1,9 @@
|
||||
import { manifests as workspaceActionManifests } from './workspace-action/manifests.js';
|
||||
import { manifests as workspaceActionMenuItemManifests } from './workspace-action-menu-item/manifests.js';
|
||||
import { manifests as workspaceBreadcrumbManifests } from './workspace-breadcrumb/manifests.js';
|
||||
|
||||
export const manifests = [...workspaceActionManifests, ...workspaceActionMenuItemManifests];
|
||||
export const manifests = [
|
||||
...workspaceActionManifests,
|
||||
...workspaceActionMenuItemManifests,
|
||||
...workspaceBreadcrumbManifests,
|
||||
];
|
||||
|
||||
@@ -99,7 +99,7 @@ export class UmbVariantSelectorElement extends UmbLitElement {
|
||||
async #observeActiveVariants() {
|
||||
if (!this.#splitViewContext) return;
|
||||
|
||||
const workspaceContext = this.#splitViewContext.getWorkspaceContext();
|
||||
const workspaceContext = this.#splitViewContext.getWorkspaceContext() as UmbDocumentWorkspaceContext;
|
||||
if (workspaceContext) {
|
||||
this.observe(
|
||||
workspaceContext.splitView.activeVariantsInfo,
|
||||
@@ -212,6 +212,7 @@ export class UmbVariantSelectorElement extends UmbLitElement {
|
||||
? html`
|
||||
<uui-button
|
||||
id="variant-selector-toggle"
|
||||
compact
|
||||
slot="append"
|
||||
popovertarget="variant-selector-popover"
|
||||
title=${this._variantTitleName}>
|
||||
@@ -223,9 +224,9 @@ export class UmbVariantSelectorElement extends UmbLitElement {
|
||||
<uui-button slot="append" compact id="variant-close" @click=${this.#closeSplitView}>
|
||||
<uui-icon name="remove"></uui-icon>
|
||||
</uui-button>
|
||||
`
|
||||
`
|
||||
: ''}
|
||||
`
|
||||
`
|
||||
: nothing
|
||||
}
|
||||
</uui-input>
|
||||
@@ -264,7 +265,7 @@ export class UmbVariantSelectorElement extends UmbLitElement {
|
||||
@click=${() => this.#openSplitView(variant)}>
|
||||
Split view
|
||||
</uui-button>
|
||||
`}
|
||||
`}
|
||||
</li>
|
||||
`,
|
||||
)}
|
||||
@@ -272,7 +273,7 @@ export class UmbVariantSelectorElement extends UmbLitElement {
|
||||
</uui-scroll-container>
|
||||
</div>
|
||||
</uui-popover-container>
|
||||
`
|
||||
`
|
||||
: nothing
|
||||
}
|
||||
</div>
|
||||
@@ -284,7 +285,6 @@ export class UmbVariantSelectorElement extends UmbLitElement {
|
||||
css`
|
||||
#name-input {
|
||||
width: 100%;
|
||||
height: 100%; /** I really don't know why this fixes the border colliding with variant-selector-toggle, but lets this solution for now */
|
||||
}
|
||||
|
||||
#variant-selector-toggle {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { UmbSaveableWorkspaceContextInterface } from '../../../../contexts/saveable-workspace-context.interface.js';
|
||||
import type { UmbSaveableWorkspaceContext } from '../../../../contexts/tokens/saveable-workspace-context.interface.js';
|
||||
import { UmbWorkspaceActionBase } from '../../workspace-action-base.controller.js';
|
||||
import { UMB_SAVEABLE_WORKSPACE_CONTEXT, type UmbWorkspaceActionArgs } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export class UmbSaveWorkspaceAction extends UmbWorkspaceActionBase<UmbSaveableWorkspaceContextInterface> {
|
||||
constructor(host: UmbControllerHost, args: UmbWorkspaceActionArgs<UmbSaveableWorkspaceContextInterface>) {
|
||||
export class UmbSaveWorkspaceAction extends UmbWorkspaceActionBase<UmbSaveableWorkspaceContext> {
|
||||
constructor(host: UmbControllerHost, args: UmbWorkspaceActionArgs<UmbSaveableWorkspaceContext>) {
|
||||
super(host, args);
|
||||
|
||||
// TODO: Could we make change label depending on the state?
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import { manifest as workspaceBreadcrumbKind } from './workspace-menu-breadcrumb/workspace-menu-breadcrumb.kind.js';
|
||||
import { manifest as variantBreadcrumbKind } from './workspace-variant-menu-breadcrumb/workspace-variant-menu-breadcrumb.kind.js';
|
||||
|
||||
export const manifests = [workspaceBreadcrumbKind, variantBreadcrumbKind];
|
||||
@@ -0,0 +1,95 @@
|
||||
import { html, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import { UMB_SECTION_CONTEXT } from '@umbraco-cms/backoffice/section';
|
||||
import type { UmbMenuStructureWorkspaceContext, UmbStructureItemModel } from '@umbraco-cms/backoffice/menu';
|
||||
|
||||
@customElement('umb-workspace-breadcrumb')
|
||||
export class UmbWorkspaceBreadcrumbElement extends UmbLitElement {
|
||||
@state()
|
||||
_name: string = '';
|
||||
|
||||
@state()
|
||||
_structure: UmbStructureItemModel[] = [];
|
||||
|
||||
// TODO: figure out the correct context type
|
||||
#workspaceContext?: any;
|
||||
#sectionContext?: typeof UMB_SECTION_CONTEXT.TYPE;
|
||||
#structureContext?: UmbMenuStructureWorkspaceContext;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_SECTION_CONTEXT, (instance) => {
|
||||
this.#sectionContext = instance;
|
||||
});
|
||||
|
||||
this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => {
|
||||
this.#workspaceContext = instance as any;
|
||||
this.#observeStructure();
|
||||
this.#observeName();
|
||||
});
|
||||
|
||||
// TODO: set up context token
|
||||
this.consumeContext('UmbMenuStructureWorkspaceContext', (instance) => {
|
||||
// TODO: get the correct interface from the context token
|
||||
this.#structureContext = instance as UmbMenuStructureWorkspaceContext;
|
||||
this.#observeStructure();
|
||||
});
|
||||
}
|
||||
|
||||
#observeStructure() {
|
||||
if (!this.#structureContext || !this.#workspaceContext) return;
|
||||
const isNew = this.#workspaceContext.getIsNew();
|
||||
|
||||
this.observe(
|
||||
this.#structureContext.structure,
|
||||
(value) => {
|
||||
// TODO: get the type from the context
|
||||
const structure = value as Array<UmbStructureItemModel>;
|
||||
this._structure = isNew ? structure : structure.slice(0, -1);
|
||||
},
|
||||
'menuStructureObserver',
|
||||
);
|
||||
}
|
||||
|
||||
#observeName() {
|
||||
this.observe(
|
||||
this.#workspaceContext?.name,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
(value) => (this._name = value || ''),
|
||||
'breadcrumbWorkspaceNameObserver',
|
||||
);
|
||||
}
|
||||
|
||||
#getHref(structureItem: UmbStructureItemModel) {
|
||||
const workspaceBasePath = `section/${this.#sectionContext?.getPathname()}/workspace/${structureItem.entityType}/edit`;
|
||||
return structureItem.isFolder ? undefined : `${workspaceBasePath}/${structureItem.unique}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<uui-breadcrumbs>
|
||||
${this._structure?.map(
|
||||
(structureItem) =>
|
||||
html`<uui-breadcrumb-item href="${ifDefined(this.#getHref(structureItem))}"
|
||||
>${structureItem.name}</uui-breadcrumb-item
|
||||
>`,
|
||||
)}
|
||||
<uui-breadcrumb-item>${this._name}</uui-breadcrumb-item>
|
||||
</uui-breadcrumbs>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [UmbTextStyles];
|
||||
}
|
||||
|
||||
export default UmbWorkspaceBreadcrumbElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-workspace-breadcrumb': UmbWorkspaceBreadcrumbElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const manifest: UmbBackofficeManifestKind = {
|
||||
type: 'kind',
|
||||
alias: 'Umb.Kind.WorkspaceFooterApp.MenuBreadcrumb',
|
||||
matchKind: 'menuBreadcrumb',
|
||||
matchType: 'workspaceFooterApp',
|
||||
manifest: {
|
||||
type: 'workspaceFooterApp',
|
||||
kind: 'menuBreadcrumb',
|
||||
element: () => import('./workspace-menu-breadcrumb.element.js'),
|
||||
weight: 1000,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,136 @@
|
||||
import { html, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import type { UmbVariantDatasetWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
import { UMB_VARIANT_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
||||
import type { UmbAppLanguageContext } from '@umbraco-cms/backoffice/language';
|
||||
import { UMB_APP_LANGUAGE_CONTEXT } from '@umbraco-cms/backoffice/language';
|
||||
import { UMB_SECTION_CONTEXT } from '@umbraco-cms/backoffice/section';
|
||||
import type { UmbVariantStructureItemModel } from '@umbraco-cms/backoffice/menu';
|
||||
|
||||
@customElement('umb-workspace-variant-menu-breadcrumb')
|
||||
export class UmbWorkspaceVariantMenuBreadcrumbElement extends UmbLitElement {
|
||||
@state()
|
||||
_name: string = '';
|
||||
|
||||
@state()
|
||||
_structure: Array<UmbVariantStructureItemModel> = [];
|
||||
|
||||
@state()
|
||||
_workspaceActiveVariantId?: UmbVariantId;
|
||||
|
||||
@state()
|
||||
_appDefaultCulture?: string;
|
||||
|
||||
#sectionContext?: typeof UMB_SECTION_CONTEXT.TYPE;
|
||||
#workspaceContext?: UmbVariantDatasetWorkspaceContext;
|
||||
#appLanguageContext?: UmbAppLanguageContext;
|
||||
#structureContext?: any;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_APP_LANGUAGE_CONTEXT, (instance) => {
|
||||
this.#appLanguageContext = instance;
|
||||
this.#observeDefaultCulture();
|
||||
});
|
||||
|
||||
this.consumeContext(UMB_SECTION_CONTEXT, (instance) => {
|
||||
this.#sectionContext = instance;
|
||||
});
|
||||
|
||||
this.consumeContext(UMB_VARIANT_WORKSPACE_CONTEXT, (instance) => {
|
||||
if (!instance) return;
|
||||
this.#workspaceContext = instance;
|
||||
this.#observeWorkspaceActiveVariant();
|
||||
this.#observeStructure();
|
||||
});
|
||||
|
||||
// TODO: set up context token
|
||||
this.consumeContext('UmbMenuStructureWorkspaceContext', (instance) => {
|
||||
if (!instance) return;
|
||||
this.#structureContext = instance;
|
||||
this.#observeStructure();
|
||||
});
|
||||
}
|
||||
|
||||
#observeStructure() {
|
||||
if (!this.#structureContext || !this.#workspaceContext) return;
|
||||
const isNew = this.#workspaceContext.getIsNew();
|
||||
|
||||
this.observe(this.#structureContext.structure, (value) => {
|
||||
// TODO: get the type from the context
|
||||
const structure = value as Array<UmbVariantStructureItemModel>;
|
||||
this._structure = isNew ? structure : structure.slice(0, -1);
|
||||
});
|
||||
}
|
||||
|
||||
#observeDefaultCulture() {
|
||||
this.observe(this.#appLanguageContext!.appDefaultLanguage, (value) => {
|
||||
this._appDefaultCulture = value?.unique;
|
||||
});
|
||||
}
|
||||
|
||||
#observeWorkspaceActiveVariant() {
|
||||
this.observe(
|
||||
this.#workspaceContext?.splitView.activeVariantsInfo,
|
||||
(value) => {
|
||||
if (!value) return;
|
||||
this._workspaceActiveVariantId = UmbVariantId.Create(value[0]);
|
||||
this.#observeActiveVariantName();
|
||||
},
|
||||
|
||||
'breadcrumbWorkspaceActiveVariantObserver',
|
||||
);
|
||||
}
|
||||
|
||||
#observeActiveVariantName() {
|
||||
this.observe(
|
||||
this.#workspaceContext?.name(this._workspaceActiveVariantId),
|
||||
(value) => (this._name = value || ''),
|
||||
'breadcrumbWorkspaceNameObserver',
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: we should move the fallback name logic to a helper class. It will be used in multiple places
|
||||
#getItemVariantName(structureItem: UmbVariantStructureItemModel) {
|
||||
const fallbackName =
|
||||
structureItem.variants.find((variant) => variant.culture === this._appDefaultCulture)?.name ??
|
||||
structureItem.variants[0].name ??
|
||||
'Unknown';
|
||||
const name = structureItem.variants.find((variant) => this._workspaceActiveVariantId?.compare(variant))?.name;
|
||||
return name ?? `(${fallbackName})`;
|
||||
}
|
||||
|
||||
#getHref(structureItem: any) {
|
||||
const workspaceBasePath = `section/${this.#sectionContext?.getPathname()}/workspace/${structureItem.entityType}/edit`;
|
||||
return structureItem.isFolder
|
||||
? undefined
|
||||
: `${workspaceBasePath}/${structureItem.unique}/${this._workspaceActiveVariantId?.culture}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<uui-breadcrumbs>
|
||||
${this._structure.map(
|
||||
(structureItem) =>
|
||||
html`<uui-breadcrumb-item href="${ifDefined(this.#getHref(structureItem))}"
|
||||
>${this.#getItemVariantName(structureItem)}</uui-breadcrumb-item
|
||||
>`,
|
||||
)}
|
||||
<uui-breadcrumb-item>${this._name}</uui-breadcrumb-item>
|
||||
</uui-breadcrumbs>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [UmbTextStyles];
|
||||
}
|
||||
|
||||
export default UmbWorkspaceVariantMenuBreadcrumbElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-workspace-variant-menu-breadcrumb': UmbWorkspaceVariantMenuBreadcrumbElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const manifest: UmbBackofficeManifestKind = {
|
||||
type: 'kind',
|
||||
alias: 'Umb.Kind.WorkspaceFooterApp.VariantMenuBreadcrumb',
|
||||
matchKind: 'variantMenuBreadcrumb',
|
||||
matchType: 'workspaceFooterApp',
|
||||
manifest: {
|
||||
type: 'workspaceFooterApp',
|
||||
kind: 'variantMenuBreadcrumb',
|
||||
element: () => import('./workspace-variant-menu-breadcrumb.element.js'),
|
||||
weight: 1000,
|
||||
},
|
||||
};
|
||||
@@ -2,14 +2,14 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { css, html, customElement, state, nothing, query } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { UmbActionExecutedEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import { UMB_ENTITY_WORKSPACE_CONTEXT, type UmbWorkspaceUniqueType } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UUIPopoverContainerElement } from '@umbraco-cms/backoffice/external/uui';
|
||||
@customElement('umb-workspace-entity-action-menu')
|
||||
export class UmbWorkspaceEntityActionMenuElement extends UmbLitElement {
|
||||
private _workspaceContext?: typeof UMB_WORKSPACE_CONTEXT.TYPE;
|
||||
private _workspaceContext?: typeof UMB_ENTITY_WORKSPACE_CONTEXT.TYPE;
|
||||
|
||||
@state()
|
||||
private _unique?: string;
|
||||
private _unique?: UmbWorkspaceUniqueType;
|
||||
|
||||
@state()
|
||||
private _entityType?: string;
|
||||
@@ -23,7 +23,7 @@ export class UmbWorkspaceEntityActionMenuElement extends UmbLitElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_WORKSPACE_CONTEXT, (context) => {
|
||||
this.consumeContext(UMB_ENTITY_WORKSPACE_CONTEXT, (context) => {
|
||||
this._workspaceContext = context;
|
||||
this._observeInfo();
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UMB_SAVEABLE_WORKSPACE_CONTEXT } from '../../contexts/saveable-workspace.context-token.js';
|
||||
import { UMB_SAVEABLE_WORKSPACE_CONTEXT } from '../../contexts/tokens/saveable-workspace.context-token.js';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UMB_WORKSPACE_CONTEXT, type UmbWorkspaceContextInterface } from '../contexts/index.js';
|
||||
import { UMB_WORKSPACE_CONTEXT, type UmbWorkspaceContext } from '../contexts/index.js';
|
||||
import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type {
|
||||
ManifestCondition,
|
||||
@@ -15,12 +15,11 @@ export class UmbWorkspaceAliasCondition
|
||||
constructor(host: UmbControllerHost, args: UmbConditionControllerArguments<WorkspaceAliasConditionConfig>) {
|
||||
super(host, args);
|
||||
|
||||
let permissionCheck: ((context: UmbWorkspaceContextInterface) => boolean) | undefined = undefined;
|
||||
let permissionCheck: ((context: UmbWorkspaceContext) => boolean) | undefined = undefined;
|
||||
if (this.config.match) {
|
||||
permissionCheck = (context: UmbWorkspaceContextInterface) => context.workspaceAlias === this.config.match;
|
||||
permissionCheck = (context: UmbWorkspaceContext) => context.workspaceAlias === this.config.match;
|
||||
} else if (this.config.oneOf) {
|
||||
permissionCheck = (context: UmbWorkspaceContextInterface) =>
|
||||
this.config.oneOf!.indexOf(context.workspaceAlias) !== -1;
|
||||
permissionCheck = (context: UmbWorkspaceContext) => this.config.oneOf!.indexOf(context.workspaceAlias) !== -1;
|
||||
}
|
||||
|
||||
if (permissionCheck !== undefined) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UMB_WORKSPACE_COLLECTION_CONTEXT } from '../contexts/workspace-collection-context.token.js';
|
||||
import { UMB_COLLECTION_WORKSPACE_CONTEXT } from '../contexts/tokens/collection-workspace.context-token.js';
|
||||
import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type {
|
||||
ManifestCondition,
|
||||
@@ -15,7 +15,7 @@ export class UmbWorkspaceHasCollectionCondition
|
||||
constructor(host: UmbControllerHost, args: UmbConditionControllerArguments<WorkspaceHasCollectionConditionConfig>) {
|
||||
super(host, args);
|
||||
|
||||
this.consumeContext(UMB_WORKSPACE_COLLECTION_CONTEXT, (context) => {
|
||||
this.consumeContext(UMB_COLLECTION_WORKSPACE_CONTEXT, (context) => {
|
||||
this.observe(
|
||||
context.contentTypeHasCollection,
|
||||
(hasCollection) => {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { UMB_WORKSPACE_CONTEXT } from './workspace-context.token.js';
|
||||
import type { UmbWorkspaceContextInterface } from './workspace-context.interface.js';
|
||||
import { UMB_WORKSPACE_CONTEXT } from './tokens/workspace.context-token.js';
|
||||
import type { UmbWorkspaceContext } from './tokens/workspace-context.interface.js';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import type { ManifestWorkspace } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export abstract class UmbDefaultWorkspaceContext
|
||||
extends UmbContextBase<UmbDefaultWorkspaceContext>
|
||||
implements UmbWorkspaceContextInterface
|
||||
implements UmbWorkspaceContext
|
||||
{
|
||||
public workspaceAlias!: string;
|
||||
#entityType!: string;
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
export * from './default-workspace.context.js';
|
||||
export * from './editable-workspace-context-base.js';
|
||||
export * from './property-structure-workspace-context.interface.js';
|
||||
export * from './publishable-workspace-context.interface.js';
|
||||
export * from './publishable-workspace.context-token.js';
|
||||
export * from './routable-workspace-context.interface.js';
|
||||
export * from './saveable-workspace-context.interface.js';
|
||||
export * from './saveable-workspace.context-token.js';
|
||||
export * from './variant-workspace-context.token.js';
|
||||
export * from './workspace-collection-context.interface.js';
|
||||
export * from './workspace-collection-context.token.js';
|
||||
export * from './workspace-context.interface.js';
|
||||
export * from './workspace-context.token.js';
|
||||
export * from './workspace-invariantable-context.interface.js';
|
||||
export * from './workspace-variantable-context.interface.js';
|
||||
export * from './saveable-workspace-context-base.js';
|
||||
export * from './tokens/index.js';
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import type { UmbSaveableWorkspaceContextInterface } from './saveable-workspace-context.interface.js';
|
||||
|
||||
export interface UmbPublishableWorkspaceContextInterface extends UmbSaveableWorkspaceContextInterface {
|
||||
saveAndPublish(): Promise<void>;
|
||||
publish(): Promise<void>;
|
||||
unpublish(): Promise<void>;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import type {
|
||||
UmbPublishableWorkspaceContextInterface,
|
||||
UmbSaveableWorkspaceContextInterface,
|
||||
} from '@umbraco-cms/backoffice/workspace';
|
||||
|
||||
export const UMB_PUBLISHABLE_WORKSPACE_CONTEXT = new UmbContextToken<
|
||||
UmbSaveableWorkspaceContextInterface,
|
||||
UmbPublishableWorkspaceContextInterface
|
||||
>(
|
||||
'UmbWorkspaceContext',
|
||||
undefined,
|
||||
(context): context is UmbPublishableWorkspaceContextInterface => (context as any).publish !== undefined,
|
||||
);
|
||||
@@ -1,6 +0,0 @@
|
||||
import type { UmbWorkspaceRouteManager } from '../index.js';
|
||||
import type { UmbWorkspaceContextInterface } from './workspace-context.interface.js';
|
||||
|
||||
export interface UmbRoutableWorkspaceContext extends UmbWorkspaceContextInterface {
|
||||
readonly routes: UmbWorkspaceRouteManager;
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
import { UMB_WORKSPACE_CONTEXT } from './workspace-context.token.js';
|
||||
import type { UmbSaveableWorkspaceContextInterface } from './saveable-workspace-context.interface.js';
|
||||
import { UmbWorkspaceRouteManager } from '../controllers/workspace-route-manager.controller.js';
|
||||
import { UMB_WORKSPACE_CONTEXT } from './tokens/workspace.context-token.js';
|
||||
import type { UmbSaveableWorkspaceContext } from './tokens/saveable-workspace-context.interface.js';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api';
|
||||
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';
|
||||
|
||||
export abstract class UmbEditableWorkspaceContextBase<WorkspaceDataModelType>
|
||||
extends UmbContextBase<UmbEditableWorkspaceContextBase<WorkspaceDataModelType>>
|
||||
implements UmbSaveableWorkspaceContextInterface
|
||||
export abstract class UmbSaveableWorkspaceContextBase<WorkspaceDataModelType>
|
||||
extends UmbContextBase<UmbSaveableWorkspaceContextBase<WorkspaceDataModelType>>
|
||||
implements UmbSaveableWorkspaceContext
|
||||
{
|
||||
public readonly workspaceAlias: string;
|
||||
|
||||
@@ -18,6 +20,8 @@ export abstract class UmbEditableWorkspaceContextBase<WorkspaceDataModelType>
|
||||
#isNew = new UmbBooleanState(undefined);
|
||||
isNew = this.#isNew.asObservable();
|
||||
|
||||
readonly routes = new UmbWorkspaceRouteManager(this);
|
||||
|
||||
/*
|
||||
Concept notes: [NL]
|
||||
Considerations are, if we bring a dirty state (observable) we need to maintain it all the time.
|
||||
@@ -56,8 +60,17 @@ export abstract class UmbEditableWorkspaceContextBase<WorkspaceDataModelType>
|
||||
}
|
||||
|
||||
//abstract getIsDirty(): Promise<boolean>;
|
||||
abstract getUnique(): string | undefined; // TODO: Consider if this should go away/be renamed? now that we have getUnique()
|
||||
abstract getUnique(): string | undefined;
|
||||
abstract getEntityType(): string;
|
||||
abstract getData(): WorkspaceDataModelType | undefined;
|
||||
abstract save(): Promise<void>;
|
||||
abstract readonly unique: Observable<string | null | undefined>;
|
||||
}
|
||||
|
||||
/*
|
||||
* @deprecated Use UmbSaveableWorkspaceContextBase instead — Will be removed before RC.
|
||||
* TODO: Delete before RC.
|
||||
*/
|
||||
export abstract class UmbEditableWorkspaceContextBase<
|
||||
WorkspaceDataModelType,
|
||||
> extends UmbSaveableWorkspaceContextBase<WorkspaceDataModelType> {}
|
||||
@@ -1,10 +0,0 @@
|
||||
import type { UmbWorkspaceContextInterface } from './workspace-context.interface.js';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
|
||||
export interface UmbSaveableWorkspaceContextInterface extends UmbWorkspaceContextInterface {
|
||||
isNew: Observable<boolean | undefined>;
|
||||
getIsNew(): boolean | undefined;
|
||||
save(): Promise<void>;
|
||||
setValidationErrors?(errorMap: any): void; // TODO: temp solution to bubble validation errors to the UI
|
||||
destroy(): void;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { UmbWorkspaceContextInterface, UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace';
|
||||
|
||||
export const UMB_SAVEABLE_WORKSPACE_CONTEXT = new UmbContextToken<
|
||||
UmbWorkspaceContextInterface,
|
||||
UmbSaveableWorkspaceContextInterface
|
||||
>(
|
||||
'UmbWorkspaceContext',
|
||||
undefined,
|
||||
(context): context is UmbSaveableWorkspaceContextInterface =>
|
||||
(context as UmbSaveableWorkspaceContextInterface).getIsNew !== undefined,
|
||||
);
|
||||
@@ -1,10 +1,16 @@
|
||||
import type { UmbWorkspaceContextInterface } from './workspace-context.interface.js';
|
||||
import type { UmbEntityWorkspaceContext } from './entity-workspace-context.interface.js';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import type { UmbContentTypeModel, UmbContentTypeStructureManager } from '@umbraco-cms/backoffice/content-type';
|
||||
|
||||
export interface UmbWorkspaceCollectionContextInterface<T extends UmbContentTypeModel>
|
||||
extends UmbWorkspaceContextInterface {
|
||||
export interface UmbCollectionWorkspaceContext<T extends UmbContentTypeModel> extends UmbEntityWorkspaceContext {
|
||||
contentTypeHasCollection: Observable<boolean>;
|
||||
getCollectionAlias(): string;
|
||||
structure: UmbContentTypeStructureManager<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use UmbCollectionWorkspaceContextInterface instead — Will be removed before RC.
|
||||
* TODO: Delete before RC.
|
||||
*/
|
||||
export interface UmbWorkspaceCollectionContextInterface<T extends UmbContentTypeModel>
|
||||
extends UmbCollectionWorkspaceContext<T> {}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { UmbContentTypeModel } from '@umbraco-cms/backoffice/content-type';
|
||||
import type { UmbWorkspaceContext, UmbCollectionWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
|
||||
export const UMB_COLLECTION_WORKSPACE_CONTEXT = new UmbContextToken<
|
||||
UmbWorkspaceContext,
|
||||
UmbCollectionWorkspaceContext<UmbContentTypeModel>
|
||||
>(
|
||||
'UmbWorkspaceContext',
|
||||
undefined,
|
||||
(context): context is UmbCollectionWorkspaceContext<UmbContentTypeModel> =>
|
||||
(context as UmbCollectionWorkspaceContext<UmbContentTypeModel>).contentTypeHasCollection !== undefined,
|
||||
);
|
||||
|
||||
/**
|
||||
* @deprecated Use `UMB_COLLECTION_WORKSPACE_CONTEXT` instead. This token will be removed in the RC version.
|
||||
* TODO: Remove in RC
|
||||
*/
|
||||
export const UMB_WORKSPACE_COLLECTION_CONTEXT = UMB_COLLECTION_WORKSPACE_CONTEXT;
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { UmbWorkspaceContext } from './workspace-context.interface.js';
|
||||
import type { UmbWorkspaceUniqueType } from './../../types.js';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
|
||||
export interface UmbEntityWorkspaceContext extends UmbWorkspaceContext {
|
||||
unique: Observable<UmbWorkspaceUniqueType | undefined>;
|
||||
getUnique(): UmbWorkspaceUniqueType | undefined;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { UmbWorkspaceContext } from './workspace-context.interface.js';
|
||||
import type { UmbEntityWorkspaceContext } from './entity-workspace-context.interface.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
|
||||
export const UMB_ENTITY_WORKSPACE_CONTEXT = new UmbContextToken<UmbWorkspaceContext, UmbEntityWorkspaceContext>(
|
||||
'UmbWorkspaceContext',
|
||||
undefined,
|
||||
(context): context is UmbEntityWorkspaceContext => (context as any).getUnique !== undefined,
|
||||
);
|
||||
@@ -0,0 +1,16 @@
|
||||
export * from './collection-workspace.context-token.js';
|
||||
export * from './entity-workspace.context-token.js';
|
||||
export * from './publishable-workspace.context-token.js';
|
||||
export * from './routable-workspace.context-token.js';
|
||||
export * from './saveable-workspace.context-token.js';
|
||||
export * from './variant-workspace.context-token.js';
|
||||
export * from './workspace.context-token.js';
|
||||
export type * from './collection-workspace-context.interface.js';
|
||||
export type * from './entity-workspace-context.interface.js';
|
||||
export type * from './invariant-dataset-workspace-context.interface.js';
|
||||
export type * from './property-structure-workspace-context.interface.js';
|
||||
export type * from './publishable-workspace-context.interface.js';
|
||||
export type * from './routable-workspace-context.interface.js';
|
||||
export type * from './saveable-workspace-context.interface.js';
|
||||
export type * from './variant-dataset-workspace-context.interface.js';
|
||||
export type * from './workspace-context.interface.js';
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { UmbVariantId } from '../../variant/variant-id.class.js';
|
||||
import type { UmbPropertyDatasetContext } from '../../property/property-dataset/property-dataset-context.interface.js';
|
||||
import type { UmbSaveableWorkspaceContextInterface } from './saveable-workspace-context.interface.js';
|
||||
import type { UmbVariantId } from '../../../variant/variant-id.class.js';
|
||||
import type { UmbPropertyDatasetContext } from '../../../property/property-dataset/property-dataset-context.interface.js';
|
||||
import type { UmbSaveableWorkspaceContext } from './saveable-workspace-context.interface.js';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
|
||||
export interface UmbInvariantableWorkspaceContextInterface extends UmbSaveableWorkspaceContextInterface {
|
||||
export interface UmbInvariantDatasetWorkspaceContext extends UmbSaveableWorkspaceContext {
|
||||
// Name:
|
||||
name: Observable<string | undefined>;
|
||||
getName(): string | undefined;
|
||||
@@ -17,3 +17,9 @@ export interface UmbInvariantableWorkspaceContextInterface extends UmbSaveableWo
|
||||
|
||||
createPropertyDatasetContext(host: UmbControllerHost, variantId?: UmbVariantId): UmbPropertyDatasetContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use UmbInvariantWorkspaceContextInterface instead — Will be removed before RC.
|
||||
* TODO: Delete before RC.
|
||||
*/
|
||||
export interface UmbInvariantableWorkspaceContextInterface extends UmbInvariantDatasetWorkspaceContext {}
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { UmbWorkspaceContextInterface } from './workspace-context.interface.js';
|
||||
import type { UmbEntityWorkspaceContext } from './entity-workspace-context.interface.js';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import type { ValueModelBaseModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
|
||||
export interface UmbPropertyStructureWorkspaceContextInterface extends UmbWorkspaceContextInterface {
|
||||
export interface UmbPropertyStructureWorkspaceContext extends UmbEntityWorkspaceContext {
|
||||
propertyStructureById(id: string): Promise<Observable<ValueModelBaseModel | undefined>>;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { UmbWorkspaceContext } from './workspace-context.interface.js';
|
||||
import type { UmbPropertyStructureWorkspaceContext } from './property-structure-workspace-context.interface.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
|
||||
export const UMB_PROPERTY_STRUCTURE_WORKSPACE_CONTEXT = new UmbContextToken<
|
||||
UmbWorkspaceContext,
|
||||
UmbPropertyStructureWorkspaceContext
|
||||
>(
|
||||
'UmbWorkspaceContext',
|
||||
undefined,
|
||||
(context): context is UmbPropertyStructureWorkspaceContext => 'propertyStructureById' in context,
|
||||
);
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { UmbSaveableWorkspaceContext } from './saveable-workspace-context.interface.js';
|
||||
|
||||
export interface UmbPublishableWorkspaceContext extends UmbSaveableWorkspaceContext {
|
||||
saveAndPublish(): Promise<void>;
|
||||
publish(): Promise<void>;
|
||||
unpublish(): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { UmbPublishableWorkspaceContext, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
|
||||
export const UMB_PUBLISHABLE_WORKSPACE_CONTEXT = new UmbContextToken<
|
||||
UmbWorkspaceContext,
|
||||
UmbPublishableWorkspaceContext
|
||||
>('UmbWorkspaceContext', undefined, (context): context is UmbPublishableWorkspaceContext => 'publish' in context);
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { UmbWorkspaceRouteManager } from '../../index.js';
|
||||
import type { UmbWorkspaceContext } from './workspace-context.interface.js';
|
||||
|
||||
export interface UmbRoutableWorkspaceContext extends UmbWorkspaceContext {
|
||||
readonly routes: UmbWorkspaceRouteManager;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { UmbRoutableWorkspaceContext } from './routable-workspace-context.interface.js';
|
||||
import type { UmbWorkspaceContext } from './workspace-context.interface.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
|
||||
export const UMB_ROUTABLE_WORKSPACE_CONTEXT = new UmbContextToken<UmbWorkspaceContext, UmbRoutableWorkspaceContext>(
|
||||
'UmbWorkspaceContext',
|
||||
undefined,
|
||||
(context): context is UmbRoutableWorkspaceContext => 'routes' in context,
|
||||
);
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { UmbEntityWorkspaceContext } from './entity-workspace-context.interface.js';
|
||||
import type { UmbRoutableWorkspaceContext } from './routable-workspace-context.interface.js';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
|
||||
export interface UmbSaveableWorkspaceContext extends UmbEntityWorkspaceContext, UmbRoutableWorkspaceContext {
|
||||
isNew: Observable<boolean | undefined>;
|
||||
getIsNew(): boolean | undefined;
|
||||
save(): Promise<void>;
|
||||
destroy(): void;
|
||||
}
|
||||
/**
|
||||
* @deprecated Use `UmbSaveableWorkspaceContext` instead. This token will be removed in the RC version.
|
||||
* TODO: Remove in RC
|
||||
*/
|
||||
export interface UmbSaveableWorkspaceContextInterface extends UmbSaveableWorkspaceContext {}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { UmbWorkspaceContext, UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
|
||||
export const UMB_SAVEABLE_WORKSPACE_CONTEXT = new UmbContextToken<UmbWorkspaceContext, UmbSaveableWorkspaceContext>(
|
||||
'UmbWorkspaceContext',
|
||||
undefined,
|
||||
(context): context is UmbSaveableWorkspaceContext => 'getIsNew' in context,
|
||||
);
|
||||
@@ -1,15 +1,16 @@
|
||||
import type { UmbWorkspaceSplitViewManager } from '../controllers/workspace-split-view-manager.controller.js';
|
||||
import type { UmbPropertyDatasetContext } from '../../property/property-dataset/property-dataset-context.interface.js';
|
||||
import type { UmbSaveableWorkspaceContextInterface } from './saveable-workspace-context.interface.js';
|
||||
import type { UmbWorkspaceSplitViewManager } from '../../controllers/workspace-split-view-manager.controller.js';
|
||||
import type { UmbPropertyDatasetContext } from '../../../property/property-dataset/property-dataset-context.interface.js';
|
||||
import type { UmbSaveableWorkspaceContext } from './saveable-workspace-context.interface.js';
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import type { UmbVariantId, UmbVariantModel, UmbVariantOptionModel } from '@umbraco-cms/backoffice/variant';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export interface UmbVariantableWorkspaceContextInterface<VariantType extends UmbVariantModel = UmbVariantModel>
|
||||
extends UmbSaveableWorkspaceContextInterface {
|
||||
export interface UmbVariantDatasetWorkspaceContext<VariantType extends UmbVariantModel = UmbVariantModel>
|
||||
extends UmbSaveableWorkspaceContext {
|
||||
// Name:
|
||||
getName(variantId?: UmbVariantId): string | undefined;
|
||||
setName(name: string, variantId?: UmbVariantId): void;
|
||||
name(variantId?: UmbVariantId): Observable<string>;
|
||||
|
||||
// Variant:
|
||||
variants: Observable<Array<UmbVariantModel>>;
|
||||
@@ -18,14 +19,19 @@ export interface UmbVariantableWorkspaceContextInterface<VariantType extends Umb
|
||||
getVariant(variantId: UmbVariantId): UmbVariantModel | undefined;
|
||||
|
||||
// Property:
|
||||
// This one is async cause it needs to structure to provide this data:
|
||||
// This one is async cause it needs to structure to provide this data: [NL]
|
||||
propertyValueByAlias<ReturnValue = unknown>(
|
||||
alias: string,
|
||||
variantId?: UmbVariantId,
|
||||
): Promise<Observable<ReturnValue | undefined> | undefined>;
|
||||
getPropertyValue<ReturnValue = unknown>(alias: string, variantId?: UmbVariantId): ReturnValue | undefined;
|
||||
setPropertyValue(alias: string, value: unknown, variantId?: UmbVariantId): Promise<void>;
|
||||
//propertyDataByAlias(alias: string, variantId?: UmbVariantId): Observable<ValueModelBaseModel | undefined>;
|
||||
|
||||
createPropertyDatasetContext(host: UmbControllerHost, variantId?: UmbVariantId): UmbPropertyDatasetContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use UmbVariantWorkspaceContextInterface instead — Will be removed before RC.
|
||||
* TODO: Delete before RC.
|
||||
*/
|
||||
export interface UmbVariantableWorkspaceContextInterface extends UmbVariantDatasetWorkspaceContext {}
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { UmbWorkspaceContext } from './workspace-context.interface.js';
|
||||
import type { UmbVariantDatasetWorkspaceContext } from './variant-dataset-workspace-context.interface.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
|
||||
export const UMB_VARIANT_WORKSPACE_CONTEXT = new UmbContextToken<
|
||||
UmbWorkspaceContext,
|
||||
UmbVariantDatasetWorkspaceContext
|
||||
>('UmbWorkspaceContext', undefined, (context): context is UmbVariantDatasetWorkspaceContext => 'variants' in context);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user