diff --git a/src/Umbraco.Web.UI.Client/examples/block-custom-view/block-custom-view.ts b/src/Umbraco.Web.UI.Client/examples/block-custom-view/block-custom-view.ts index 40b88d0ce9..e564f537a6 100644 --- a/src/Umbraco.Web.UI.Client/examples/block-custom-view/block-custom-view.ts +++ b/src/Umbraco.Web.UI.Client/examples/block-custom-view/block-custom-view.ts @@ -11,11 +11,15 @@ export class ExampleBlockCustomView extends UmbElementMixin(LitElement) implemen @property({ attribute: false }) content?: UmbBlockDataType; + @property({ attribute: false }) + settings?: UmbBlockDataType; + override render() { return html` -
+
My Custom View

Headline: ${this.content?.headline}

+

Alignment: ${this.settings?.blockAlignment}

`; } @@ -31,6 +35,13 @@ export class ExampleBlockCustomView extends UmbElementMixin(LitElement) implemen border-radius: 9px; padding: 12px; } + + .align-center { + text-align: center; + } + .align-right { + text-align: right; + } `, ]; } diff --git a/src/Umbraco.Web.UI.Client/examples/block-custom-view/index.ts b/src/Umbraco.Web.UI.Client/examples/block-custom-view/index.ts index e50355a08b..da81e169ac 100644 --- a/src/Umbraco.Web.UI.Client/examples/block-custom-view/index.ts +++ b/src/Umbraco.Web.UI.Client/examples/block-custom-view/index.ts @@ -7,6 +7,6 @@ export const manifests: Array = [ name: 'Block Editor Custom View Test', element: () => import('./block-custom-view.js'), forContentTypeAlias: 'headlineUmbracoDemoBlock', - forBlockEditor: 'block-grid', + forBlockEditor: 'block-list', }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/services.gen.ts b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/services.gen.ts index 7a132b3073..c6edaee403 100644 --- a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/services.gen.ts +++ b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/services.gen.ts @@ -3,7 +3,7 @@ import type { CancelablePromise } from './core/CancelablePromise'; import { OpenAPI } from './core/OpenAPI'; import { request as __request } from './core/request'; -import type { GetCultureData, GetCultureResponse, PostDataTypeData, PostDataTypeResponse, GetDataTypeByIdData, GetDataTypeByIdResponse, DeleteDataTypeByIdData, DeleteDataTypeByIdResponse, PutDataTypeByIdData, PutDataTypeByIdResponse, PostDataTypeByIdCopyData, PostDataTypeByIdCopyResponse, GetDataTypeByIdIsUsedData, GetDataTypeByIdIsUsedResponse, PutDataTypeByIdMoveData, PutDataTypeByIdMoveResponse, GetDataTypeByIdReferencesData, GetDataTypeByIdReferencesResponse, GetDataTypeConfigurationResponse, PostDataTypeFolderData, PostDataTypeFolderResponse, GetDataTypeFolderByIdData, GetDataTypeFolderByIdResponse, DeleteDataTypeFolderByIdData, DeleteDataTypeFolderByIdResponse, PutDataTypeFolderByIdData, PutDataTypeFolderByIdResponse, GetFilterDataTypeData, GetFilterDataTypeResponse, GetItemDataTypeData, GetItemDataTypeResponse, GetItemDataTypeSearchData, GetItemDataTypeSearchResponse, GetTreeDataTypeAncestorsData, GetTreeDataTypeAncestorsResponse, GetTreeDataTypeChildrenData, GetTreeDataTypeChildrenResponse, GetTreeDataTypeRootData, GetTreeDataTypeRootResponse, GetDictionaryData, GetDictionaryResponse, PostDictionaryData, PostDictionaryResponse, GetDictionaryByIdData, GetDictionaryByIdResponse, DeleteDictionaryByIdData, DeleteDictionaryByIdResponse, PutDictionaryByIdData, PutDictionaryByIdResponse, GetDictionaryByIdExportData, GetDictionaryByIdExportResponse, PutDictionaryByIdMoveData, PutDictionaryByIdMoveResponse, PostDictionaryImportData, PostDictionaryImportResponse, GetItemDictionaryData, GetItemDictionaryResponse, GetTreeDictionaryAncestorsData, GetTreeDictionaryAncestorsResponse, GetTreeDictionaryChildrenData, GetTreeDictionaryChildrenResponse, GetTreeDictionaryRootData, GetTreeDictionaryRootResponse, PostDocumentBlueprintData, PostDocumentBlueprintResponse, GetDocumentBlueprintByIdData, GetDocumentBlueprintByIdResponse, DeleteDocumentBlueprintByIdData, DeleteDocumentBlueprintByIdResponse, PutDocumentBlueprintByIdData, PutDocumentBlueprintByIdResponse, PutDocumentBlueprintByIdMoveData, PutDocumentBlueprintByIdMoveResponse, PostDocumentBlueprintFolderData, PostDocumentBlueprintFolderResponse, GetDocumentBlueprintFolderByIdData, GetDocumentBlueprintFolderByIdResponse, DeleteDocumentBlueprintFolderByIdData, DeleteDocumentBlueprintFolderByIdResponse, PutDocumentBlueprintFolderByIdData, PutDocumentBlueprintFolderByIdResponse, PostDocumentBlueprintFromDocumentData, PostDocumentBlueprintFromDocumentResponse, GetItemDocumentBlueprintData, GetItemDocumentBlueprintResponse, GetTreeDocumentBlueprintAncestorsData, GetTreeDocumentBlueprintAncestorsResponse, GetTreeDocumentBlueprintChildrenData, GetTreeDocumentBlueprintChildrenResponse, GetTreeDocumentBlueprintRootData, GetTreeDocumentBlueprintRootResponse, PostDocumentTypeData, PostDocumentTypeResponse, GetDocumentTypeByIdData, GetDocumentTypeByIdResponse, DeleteDocumentTypeByIdData, DeleteDocumentTypeByIdResponse, PutDocumentTypeByIdData, PutDocumentTypeByIdResponse, GetDocumentTypeByIdAllowedChildrenData, GetDocumentTypeByIdAllowedChildrenResponse, GetDocumentTypeByIdBlueprintData, GetDocumentTypeByIdBlueprintResponse, GetDocumentTypeByIdCompositionReferencesData, GetDocumentTypeByIdCompositionReferencesResponse, PostDocumentTypeByIdCopyData, PostDocumentTypeByIdCopyResponse, GetDocumentTypeByIdExportData, GetDocumentTypeByIdExportResponse, PutDocumentTypeByIdImportData, PutDocumentTypeByIdImportResponse, PutDocumentTypeByIdMoveData, PutDocumentTypeByIdMoveResponse, GetDocumentTypeAllowedAtRootData, GetDocumentTypeAllowedAtRootResponse, PostDocumentTypeAvailableCompositionsData, PostDocumentTypeAvailableCompositionsResponse, GetDocumentTypeConfigurationResponse, PostDocumentTypeFolderData, PostDocumentTypeFolderResponse, GetDocumentTypeFolderByIdData, GetDocumentTypeFolderByIdResponse, DeleteDocumentTypeFolderByIdData, DeleteDocumentTypeFolderByIdResponse, PutDocumentTypeFolderByIdData, PutDocumentTypeFolderByIdResponse, PostDocumentTypeImportData, PostDocumentTypeImportResponse, GetItemDocumentTypeData, GetItemDocumentTypeResponse, GetItemDocumentTypeSearchData, GetItemDocumentTypeSearchResponse, GetTreeDocumentTypeAncestorsData, GetTreeDocumentTypeAncestorsResponse, GetTreeDocumentTypeChildrenData, GetTreeDocumentTypeChildrenResponse, GetTreeDocumentTypeRootData, GetTreeDocumentTypeRootResponse, GetDocumentVersionData, GetDocumentVersionResponse, GetDocumentVersionByIdData, GetDocumentVersionByIdResponse, PutDocumentVersionByIdPreventCleanupData, PutDocumentVersionByIdPreventCleanupResponse, PostDocumentVersionByIdRollbackData, PostDocumentVersionByIdRollbackResponse, GetCollectionDocumentByIdData, GetCollectionDocumentByIdResponse, PostDocumentData, PostDocumentResponse, GetDocumentByIdData, GetDocumentByIdResponse, DeleteDocumentByIdData, DeleteDocumentByIdResponse, PutDocumentByIdData, PutDocumentByIdResponse, GetDocumentByIdAuditLogData, GetDocumentByIdAuditLogResponse, PostDocumentByIdCopyData, PostDocumentByIdCopyResponse, GetDocumentByIdDomainsData, GetDocumentByIdDomainsResponse, PutDocumentByIdDomainsData, PutDocumentByIdDomainsResponse, PutDocumentByIdMoveData, PutDocumentByIdMoveResponse, PutDocumentByIdMoveToRecycleBinData, PutDocumentByIdMoveToRecycleBinResponse, GetDocumentByIdNotificationsData, GetDocumentByIdNotificationsResponse, PutDocumentByIdNotificationsData, PutDocumentByIdNotificationsResponse, PostDocumentByIdPublicAccessData, PostDocumentByIdPublicAccessResponse, DeleteDocumentByIdPublicAccessData, DeleteDocumentByIdPublicAccessResponse, GetDocumentByIdPublicAccessData, GetDocumentByIdPublicAccessResponse, PutDocumentByIdPublicAccessData, PutDocumentByIdPublicAccessResponse, PutDocumentByIdPublishData, PutDocumentByIdPublishResponse, PutDocumentByIdPublishWithDescendantsData, PutDocumentByIdPublishWithDescendantsResponse, GetDocumentByIdReferencedByData, GetDocumentByIdReferencedByResponse, GetDocumentByIdReferencedDescendantsData, GetDocumentByIdReferencedDescendantsResponse, PutDocumentByIdUnpublishData, PutDocumentByIdUnpublishResponse, PutDocumentByIdValidateData, PutDocumentByIdValidateResponse, GetDocumentAreReferencedData, GetDocumentAreReferencedResponse, GetDocumentConfigurationResponse, PutDocumentSortData, PutDocumentSortResponse, GetDocumentUrlsData, GetDocumentUrlsResponse, PostDocumentValidateData, PostDocumentValidateResponse, GetItemDocumentData, GetItemDocumentResponse, GetItemDocumentSearchData, GetItemDocumentSearchResponse, DeleteRecycleBinDocumentResponse, DeleteRecycleBinDocumentByIdData, DeleteRecycleBinDocumentByIdResponse, GetRecycleBinDocumentByIdOriginalParentData, GetRecycleBinDocumentByIdOriginalParentResponse, PutRecycleBinDocumentByIdRestoreData, PutRecycleBinDocumentByIdRestoreResponse, GetRecycleBinDocumentChildrenData, GetRecycleBinDocumentChildrenResponse, GetRecycleBinDocumentRootData, GetRecycleBinDocumentRootResponse, GetTreeDocumentAncestorsData, GetTreeDocumentAncestorsResponse, GetTreeDocumentChildrenData, GetTreeDocumentChildrenResponse, GetTreeDocumentRootData, GetTreeDocumentRootResponse, PostDynamicRootQueryData, PostDynamicRootQueryResponse, GetDynamicRootStepsResponse, GetHealthCheckGroupData, GetHealthCheckGroupResponse, GetHealthCheckGroupByNameData, GetHealthCheckGroupByNameResponse, PostHealthCheckGroupByNameCheckData, PostHealthCheckGroupByNameCheckResponse, PostHealthCheckExecuteActionData, PostHealthCheckExecuteActionResponse, GetHelpData, GetHelpResponse, GetImagingResizeUrlsData, GetImagingResizeUrlsResponse, GetImportAnalyzeData, GetImportAnalyzeResponse, GetIndexerData, GetIndexerResponse, GetIndexerByIndexNameData, GetIndexerByIndexNameResponse, PostIndexerByIndexNameRebuildData, PostIndexerByIndexNameRebuildResponse, GetInstallSettingsResponse, PostInstallSetupData, PostInstallSetupResponse, PostInstallValidateDatabaseData, PostInstallValidateDatabaseResponse, GetItemLanguageData, GetItemLanguageResponse, GetItemLanguageDefaultResponse, GetLanguageData, GetLanguageResponse, PostLanguageData, PostLanguageResponse, GetLanguageByIsoCodeData, GetLanguageByIsoCodeResponse, DeleteLanguageByIsoCodeData, DeleteLanguageByIsoCodeResponse, PutLanguageByIsoCodeData, PutLanguageByIsoCodeResponse, GetLogViewerLevelData, GetLogViewerLevelResponse, GetLogViewerLevelCountData, GetLogViewerLevelCountResponse, GetLogViewerLogData, GetLogViewerLogResponse, GetLogViewerMessageTemplateData, GetLogViewerMessageTemplateResponse, GetLogViewerSavedSearchData, GetLogViewerSavedSearchResponse, PostLogViewerSavedSearchData, PostLogViewerSavedSearchResponse, GetLogViewerSavedSearchByNameData, GetLogViewerSavedSearchByNameResponse, DeleteLogViewerSavedSearchByNameData, DeleteLogViewerSavedSearchByNameResponse, GetLogViewerValidateLogsSizeData, GetLogViewerValidateLogsSizeResponse, GetManifestManifestResponse, GetManifestManifestPrivateResponse, GetManifestManifestPublicResponse, GetItemMediaTypeData, GetItemMediaTypeResponse, GetItemMediaTypeAllowedData, GetItemMediaTypeAllowedResponse, GetItemMediaTypeFoldersData, GetItemMediaTypeFoldersResponse, GetItemMediaTypeSearchData, GetItemMediaTypeSearchResponse, PostMediaTypeData, PostMediaTypeResponse, GetMediaTypeByIdData, GetMediaTypeByIdResponse, DeleteMediaTypeByIdData, DeleteMediaTypeByIdResponse, PutMediaTypeByIdData, PutMediaTypeByIdResponse, GetMediaTypeByIdAllowedChildrenData, GetMediaTypeByIdAllowedChildrenResponse, GetMediaTypeByIdCompositionReferencesData, GetMediaTypeByIdCompositionReferencesResponse, PostMediaTypeByIdCopyData, PostMediaTypeByIdCopyResponse, GetMediaTypeByIdExportData, GetMediaTypeByIdExportResponse, PutMediaTypeByIdImportData, PutMediaTypeByIdImportResponse, PutMediaTypeByIdMoveData, PutMediaTypeByIdMoveResponse, GetMediaTypeAllowedAtRootData, GetMediaTypeAllowedAtRootResponse, PostMediaTypeAvailableCompositionsData, PostMediaTypeAvailableCompositionsResponse, GetMediaTypeConfigurationResponse, PostMediaTypeFolderData, PostMediaTypeFolderResponse, GetMediaTypeFolderByIdData, GetMediaTypeFolderByIdResponse, DeleteMediaTypeFolderByIdData, DeleteMediaTypeFolderByIdResponse, PutMediaTypeFolderByIdData, PutMediaTypeFolderByIdResponse, PostMediaTypeImportData, PostMediaTypeImportResponse, GetTreeMediaTypeAncestorsData, GetTreeMediaTypeAncestorsResponse, GetTreeMediaTypeChildrenData, GetTreeMediaTypeChildrenResponse, GetTreeMediaTypeRootData, GetTreeMediaTypeRootResponse, GetCollectionMediaData, GetCollectionMediaResponse, GetItemMediaData, GetItemMediaResponse, GetItemMediaSearchData, GetItemMediaSearchResponse, PostMediaData, PostMediaResponse, GetMediaByIdData, GetMediaByIdResponse, DeleteMediaByIdData, DeleteMediaByIdResponse, PutMediaByIdData, PutMediaByIdResponse, GetMediaByIdAuditLogData, GetMediaByIdAuditLogResponse, PutMediaByIdMoveData, PutMediaByIdMoveResponse, PutMediaByIdMoveToRecycleBinData, PutMediaByIdMoveToRecycleBinResponse, GetMediaByIdReferencedByData, GetMediaByIdReferencedByResponse, GetMediaByIdReferencedDescendantsData, GetMediaByIdReferencedDescendantsResponse, PutMediaByIdValidateData, PutMediaByIdValidateResponse, GetMediaAreReferencedData, GetMediaAreReferencedResponse, GetMediaConfigurationResponse, PutMediaSortData, PutMediaSortResponse, GetMediaUrlsData, GetMediaUrlsResponse, PostMediaValidateData, PostMediaValidateResponse, DeleteRecycleBinMediaResponse, DeleteRecycleBinMediaByIdData, DeleteRecycleBinMediaByIdResponse, GetRecycleBinMediaByIdOriginalParentData, GetRecycleBinMediaByIdOriginalParentResponse, PutRecycleBinMediaByIdRestoreData, PutRecycleBinMediaByIdRestoreResponse, GetRecycleBinMediaChildrenData, GetRecycleBinMediaChildrenResponse, GetRecycleBinMediaRootData, GetRecycleBinMediaRootResponse, GetTreeMediaAncestorsData, GetTreeMediaAncestorsResponse, GetTreeMediaChildrenData, GetTreeMediaChildrenResponse, GetTreeMediaRootData, GetTreeMediaRootResponse, GetItemMemberGroupData, GetItemMemberGroupResponse, GetMemberGroupData, GetMemberGroupResponse, PostMemberGroupData, PostMemberGroupResponse, GetMemberGroupByIdData, GetMemberGroupByIdResponse, DeleteMemberGroupByIdData, DeleteMemberGroupByIdResponse, PutMemberGroupByIdData, PutMemberGroupByIdResponse, GetTreeMemberGroupRootData, GetTreeMemberGroupRootResponse, GetItemMemberTypeData, GetItemMemberTypeResponse, GetItemMemberTypeSearchData, GetItemMemberTypeSearchResponse, PostMemberTypeData, PostMemberTypeResponse, GetMemberTypeByIdData, GetMemberTypeByIdResponse, DeleteMemberTypeByIdData, DeleteMemberTypeByIdResponse, PutMemberTypeByIdData, PutMemberTypeByIdResponse, GetMemberTypeByIdCompositionReferencesData, GetMemberTypeByIdCompositionReferencesResponse, PostMemberTypeByIdCopyData, PostMemberTypeByIdCopyResponse, PostMemberTypeAvailableCompositionsData, PostMemberTypeAvailableCompositionsResponse, GetMemberTypeConfigurationResponse, GetTreeMemberTypeRootData, GetTreeMemberTypeRootResponse, GetFilterMemberData, GetFilterMemberResponse, GetItemMemberData, GetItemMemberResponse, GetItemMemberSearchData, GetItemMemberSearchResponse, PostMemberData, PostMemberResponse, GetMemberByIdData, GetMemberByIdResponse, DeleteMemberByIdData, DeleteMemberByIdResponse, PutMemberByIdData, PutMemberByIdResponse, PutMemberByIdValidateData, PutMemberByIdValidateResponse, GetMemberConfigurationResponse, PostMemberValidateData, PostMemberValidateResponse, PostModelsBuilderBuildResponse, GetModelsBuilderDashboardResponse, GetModelsBuilderStatusResponse, GetObjectTypesData, GetObjectTypesResponse, GetOembedQueryData, GetOembedQueryResponse, PostPackageByNameRunMigrationData, PostPackageByNameRunMigrationResponse, GetPackageConfigurationResponse, GetPackageCreatedData, GetPackageCreatedResponse, PostPackageCreatedData, PostPackageCreatedResponse, GetPackageCreatedByIdData, GetPackageCreatedByIdResponse, DeletePackageCreatedByIdData, DeletePackageCreatedByIdResponse, PutPackageCreatedByIdData, PutPackageCreatedByIdResponse, GetPackageCreatedByIdDownloadData, GetPackageCreatedByIdDownloadResponse, GetPackageMigrationStatusData, GetPackageMigrationStatusResponse, GetItemPartialViewData, GetItemPartialViewResponse, PostPartialViewData, PostPartialViewResponse, GetPartialViewByPathData, GetPartialViewByPathResponse, DeletePartialViewByPathData, DeletePartialViewByPathResponse, PutPartialViewByPathData, PutPartialViewByPathResponse, PutPartialViewByPathRenameData, PutPartialViewByPathRenameResponse, PostPartialViewFolderData, PostPartialViewFolderResponse, GetPartialViewFolderByPathData, GetPartialViewFolderByPathResponse, DeletePartialViewFolderByPathData, DeletePartialViewFolderByPathResponse, GetPartialViewSnippetData, GetPartialViewSnippetResponse, GetPartialViewSnippetByIdData, GetPartialViewSnippetByIdResponse, GetTreePartialViewAncestorsData, GetTreePartialViewAncestorsResponse, GetTreePartialViewChildrenData, GetTreePartialViewChildrenResponse, GetTreePartialViewRootData, GetTreePartialViewRootResponse, DeletePreviewResponse, PostPreviewResponse, GetProfilingStatusResponse, PutProfilingStatusData, PutProfilingStatusResponse, GetPropertyTypeIsUsedData, GetPropertyTypeIsUsedResponse, PostPublishedCacheCollectResponse, PostPublishedCacheRebuildResponse, PostPublishedCacheReloadResponse, GetPublishedCacheStatusResponse, GetRedirectManagementData, GetRedirectManagementResponse, GetRedirectManagementByIdData, GetRedirectManagementByIdResponse, DeleteRedirectManagementByIdData, DeleteRedirectManagementByIdResponse, GetRedirectManagementStatusResponse, PostRedirectManagementStatusData, PostRedirectManagementStatusResponse, GetItemRelationTypeData, GetItemRelationTypeResponse, GetRelationTypeData, GetRelationTypeResponse, GetRelationTypeByIdData, GetRelationTypeByIdResponse, GetRelationByRelationTypeIdData, GetRelationByRelationTypeIdResponse, GetItemScriptData, GetItemScriptResponse, PostScriptData, PostScriptResponse, GetScriptByPathData, GetScriptByPathResponse, DeleteScriptByPathData, DeleteScriptByPathResponse, PutScriptByPathData, PutScriptByPathResponse, PutScriptByPathRenameData, PutScriptByPathRenameResponse, PostScriptFolderData, PostScriptFolderResponse, GetScriptFolderByPathData, GetScriptFolderByPathResponse, DeleteScriptFolderByPathData, DeleteScriptFolderByPathResponse, GetTreeScriptAncestorsData, GetTreeScriptAncestorsResponse, GetTreeScriptChildrenData, GetTreeScriptChildrenResponse, GetTreeScriptRootData, GetTreeScriptRootResponse, GetSearcherData, GetSearcherResponse, GetSearcherBySearcherNameQueryData, GetSearcherBySearcherNameQueryResponse, GetSecurityConfigurationResponse, PostSecurityForgotPasswordData, PostSecurityForgotPasswordResponse, PostSecurityForgotPasswordResetData, PostSecurityForgotPasswordResetResponse, PostSecurityForgotPasswordVerifyData, PostSecurityForgotPasswordVerifyResponse, GetSegmentData, GetSegmentResponse, GetServerConfigurationResponse, GetServerInformationResponse, GetServerStatusResponse, GetServerTroubleshootingResponse, GetItemStaticFileData, GetItemStaticFileResponse, GetTreeStaticFileAncestorsData, GetTreeStaticFileAncestorsResponse, GetTreeStaticFileChildrenData, GetTreeStaticFileChildrenResponse, GetTreeStaticFileRootData, GetTreeStaticFileRootResponse, GetItemStylesheetData, GetItemStylesheetResponse, PostStylesheetData, PostStylesheetResponse, GetStylesheetByPathData, GetStylesheetByPathResponse, DeleteStylesheetByPathData, DeleteStylesheetByPathResponse, PutStylesheetByPathData, PutStylesheetByPathResponse, PutStylesheetByPathRenameData, PutStylesheetByPathRenameResponse, PostStylesheetFolderData, PostStylesheetFolderResponse, GetStylesheetFolderByPathData, GetStylesheetFolderByPathResponse, DeleteStylesheetFolderByPathData, DeleteStylesheetFolderByPathResponse, GetTreeStylesheetAncestorsData, GetTreeStylesheetAncestorsResponse, GetTreeStylesheetChildrenData, GetTreeStylesheetChildrenResponse, GetTreeStylesheetRootData, GetTreeStylesheetRootResponse, GetTagData, GetTagResponse, GetTelemetryData, GetTelemetryResponse, GetTelemetryLevelResponse, PostTelemetryLevelData, PostTelemetryLevelResponse, GetItemTemplateData, GetItemTemplateResponse, GetItemTemplateSearchData, GetItemTemplateSearchResponse, PostTemplateData, PostTemplateResponse, GetTemplateByIdData, GetTemplateByIdResponse, DeleteTemplateByIdData, DeleteTemplateByIdResponse, PutTemplateByIdData, PutTemplateByIdResponse, GetTemplateConfigurationResponse, PostTemplateQueryExecuteData, PostTemplateQueryExecuteResponse, GetTemplateQuerySettingsResponse, GetTreeTemplateAncestorsData, GetTreeTemplateAncestorsResponse, GetTreeTemplateChildrenData, GetTreeTemplateChildrenResponse, GetTreeTemplateRootData, GetTreeTemplateRootResponse, PostTemporaryFileData, PostTemporaryFileResponse, GetTemporaryFileByIdData, GetTemporaryFileByIdResponse, DeleteTemporaryFileByIdData, DeleteTemporaryFileByIdResponse, GetTemporaryFileConfigurationResponse, PostUpgradeAuthorizeResponse, GetUpgradeSettingsResponse, PostUserDataData, PostUserDataResponse, GetUserDataData, GetUserDataResponse, PutUserDataData, PutUserDataResponse, GetUserDataByIdData, GetUserDataByIdResponse, GetFilterUserGroupData, GetFilterUserGroupResponse, GetItemUserGroupData, GetItemUserGroupResponse, DeleteUserGroupData, DeleteUserGroupResponse, PostUserGroupData, PostUserGroupResponse, GetUserGroupData, GetUserGroupResponse, GetUserGroupByIdData, GetUserGroupByIdResponse, DeleteUserGroupByIdData, DeleteUserGroupByIdResponse, PutUserGroupByIdData, PutUserGroupByIdResponse, DeleteUserGroupByIdUsersData, DeleteUserGroupByIdUsersResponse, PostUserGroupByIdUsersData, PostUserGroupByIdUsersResponse, GetFilterUserData, GetFilterUserResponse, GetItemUserData, GetItemUserResponse, PostUserData, PostUserResponse, DeleteUserData, DeleteUserResponse, GetUserData, GetUserResponse, GetUserByIdData, GetUserByIdResponse, DeleteUserByIdData, DeleteUserByIdResponse, PutUserByIdData, PutUserByIdResponse, GetUserById2FaData, GetUserById2FaResponse, DeleteUserById2FaByProviderNameData, DeleteUserById2FaByProviderNameResponse, GetUserByIdCalculateStartNodesData, GetUserByIdCalculateStartNodesResponse, PostUserByIdChangePasswordData, PostUserByIdChangePasswordResponse, PostUserByIdResetPasswordData, PostUserByIdResetPasswordResponse, DeleteUserAvatarByIdData, DeleteUserAvatarByIdResponse, PostUserAvatarByIdData, PostUserAvatarByIdResponse, GetUserConfigurationResponse, GetUserCurrentResponse, GetUserCurrent2FaResponse, DeleteUserCurrent2FaByProviderNameData, DeleteUserCurrent2FaByProviderNameResponse, PostUserCurrent2FaByProviderNameData, PostUserCurrent2FaByProviderNameResponse, GetUserCurrent2FaByProviderNameData, GetUserCurrent2FaByProviderNameResponse, PostUserCurrentAvatarData, PostUserCurrentAvatarResponse, PostUserCurrentChangePasswordData, PostUserCurrentChangePasswordResponse, GetUserCurrentConfigurationResponse, GetUserCurrentLoginProvidersResponse, GetUserCurrentPermissionsData, GetUserCurrentPermissionsResponse, GetUserCurrentPermissionsDocumentData, GetUserCurrentPermissionsDocumentResponse, GetUserCurrentPermissionsMediaData, GetUserCurrentPermissionsMediaResponse, PostUserDisableData, PostUserDisableResponse, PostUserEnableData, PostUserEnableResponse, PostUserInviteData, PostUserInviteResponse, PostUserInviteCreatePasswordData, PostUserInviteCreatePasswordResponse, PostUserInviteResendData, PostUserInviteResendResponse, PostUserInviteVerifyData, PostUserInviteVerifyResponse, PostUserSetUserGroupsData, PostUserSetUserGroupsResponse, PostUserUnlockData, PostUserUnlockResponse, GetItemWebhookData, GetItemWebhookResponse, GetWebhookData, GetWebhookResponse, PostWebhookData, PostWebhookResponse, GetWebhookByIdData, GetWebhookByIdResponse, DeleteWebhookByIdData, DeleteWebhookByIdResponse, PutWebhookByIdData, PutWebhookByIdResponse, GetWebhookEventsData, GetWebhookEventsResponse } from './types.gen'; +import type { GetCultureData, GetCultureResponse, PostDataTypeData, PostDataTypeResponse, GetDataTypeByIdData, GetDataTypeByIdResponse, DeleteDataTypeByIdData, DeleteDataTypeByIdResponse, PutDataTypeByIdData, PutDataTypeByIdResponse, PostDataTypeByIdCopyData, PostDataTypeByIdCopyResponse, GetDataTypeByIdIsUsedData, GetDataTypeByIdIsUsedResponse, PutDataTypeByIdMoveData, PutDataTypeByIdMoveResponse, GetDataTypeByIdReferencesData, GetDataTypeByIdReferencesResponse, GetDataTypeConfigurationResponse, PostDataTypeFolderData, PostDataTypeFolderResponse, GetDataTypeFolderByIdData, GetDataTypeFolderByIdResponse, DeleteDataTypeFolderByIdData, DeleteDataTypeFolderByIdResponse, PutDataTypeFolderByIdData, PutDataTypeFolderByIdResponse, GetFilterDataTypeData, GetFilterDataTypeResponse, GetItemDataTypeData, GetItemDataTypeResponse, GetItemDataTypeSearchData, GetItemDataTypeSearchResponse, GetTreeDataTypeAncestorsData, GetTreeDataTypeAncestorsResponse, GetTreeDataTypeChildrenData, GetTreeDataTypeChildrenResponse, GetTreeDataTypeRootData, GetTreeDataTypeRootResponse, GetDictionaryData, GetDictionaryResponse, PostDictionaryData, PostDictionaryResponse, GetDictionaryByIdData, GetDictionaryByIdResponse, DeleteDictionaryByIdData, DeleteDictionaryByIdResponse, PutDictionaryByIdData, PutDictionaryByIdResponse, GetDictionaryByIdExportData, GetDictionaryByIdExportResponse, PutDictionaryByIdMoveData, PutDictionaryByIdMoveResponse, PostDictionaryImportData, PostDictionaryImportResponse, GetItemDictionaryData, GetItemDictionaryResponse, GetTreeDictionaryAncestorsData, GetTreeDictionaryAncestorsResponse, GetTreeDictionaryChildrenData, GetTreeDictionaryChildrenResponse, GetTreeDictionaryRootData, GetTreeDictionaryRootResponse, PostDocumentBlueprintData, PostDocumentBlueprintResponse, GetDocumentBlueprintByIdData, GetDocumentBlueprintByIdResponse, DeleteDocumentBlueprintByIdData, DeleteDocumentBlueprintByIdResponse, PutDocumentBlueprintByIdData, PutDocumentBlueprintByIdResponse, PutDocumentBlueprintByIdMoveData, PutDocumentBlueprintByIdMoveResponse, PostDocumentBlueprintFolderData, PostDocumentBlueprintFolderResponse, GetDocumentBlueprintFolderByIdData, GetDocumentBlueprintFolderByIdResponse, DeleteDocumentBlueprintFolderByIdData, DeleteDocumentBlueprintFolderByIdResponse, PutDocumentBlueprintFolderByIdData, PutDocumentBlueprintFolderByIdResponse, PostDocumentBlueprintFromDocumentData, PostDocumentBlueprintFromDocumentResponse, GetItemDocumentBlueprintData, GetItemDocumentBlueprintResponse, GetTreeDocumentBlueprintAncestorsData, GetTreeDocumentBlueprintAncestorsResponse, GetTreeDocumentBlueprintChildrenData, GetTreeDocumentBlueprintChildrenResponse, GetTreeDocumentBlueprintRootData, GetTreeDocumentBlueprintRootResponse, PostDocumentTypeData, PostDocumentTypeResponse, GetDocumentTypeByIdData, GetDocumentTypeByIdResponse, DeleteDocumentTypeByIdData, DeleteDocumentTypeByIdResponse, PutDocumentTypeByIdData, PutDocumentTypeByIdResponse, GetDocumentTypeByIdAllowedChildrenData, GetDocumentTypeByIdAllowedChildrenResponse, GetDocumentTypeByIdBlueprintData, GetDocumentTypeByIdBlueprintResponse, GetDocumentTypeByIdCompositionReferencesData, GetDocumentTypeByIdCompositionReferencesResponse, PostDocumentTypeByIdCopyData, PostDocumentTypeByIdCopyResponse, GetDocumentTypeByIdExportData, GetDocumentTypeByIdExportResponse, PutDocumentTypeByIdImportData, PutDocumentTypeByIdImportResponse, PutDocumentTypeByIdMoveData, PutDocumentTypeByIdMoveResponse, GetDocumentTypeAllowedAtRootData, GetDocumentTypeAllowedAtRootResponse, PostDocumentTypeAvailableCompositionsData, PostDocumentTypeAvailableCompositionsResponse, GetDocumentTypeConfigurationResponse, PostDocumentTypeFolderData, PostDocumentTypeFolderResponse, GetDocumentTypeFolderByIdData, GetDocumentTypeFolderByIdResponse, DeleteDocumentTypeFolderByIdData, DeleteDocumentTypeFolderByIdResponse, PutDocumentTypeFolderByIdData, PutDocumentTypeFolderByIdResponse, PostDocumentTypeImportData, PostDocumentTypeImportResponse, GetItemDocumentTypeData, GetItemDocumentTypeResponse, GetItemDocumentTypeSearchData, GetItemDocumentTypeSearchResponse, GetTreeDocumentTypeAncestorsData, GetTreeDocumentTypeAncestorsResponse, GetTreeDocumentTypeChildrenData, GetTreeDocumentTypeChildrenResponse, GetTreeDocumentTypeRootData, GetTreeDocumentTypeRootResponse, GetDocumentVersionData, GetDocumentVersionResponse, GetDocumentVersionByIdData, GetDocumentVersionByIdResponse, PutDocumentVersionByIdPreventCleanupData, PutDocumentVersionByIdPreventCleanupResponse, PostDocumentVersionByIdRollbackData, PostDocumentVersionByIdRollbackResponse, GetCollectionDocumentByIdData, GetCollectionDocumentByIdResponse, PostDocumentData, PostDocumentResponse, GetDocumentByIdData, GetDocumentByIdResponse, DeleteDocumentByIdData, DeleteDocumentByIdResponse, PutDocumentByIdData, PutDocumentByIdResponse, GetDocumentByIdAuditLogData, GetDocumentByIdAuditLogResponse, PostDocumentByIdCopyData, PostDocumentByIdCopyResponse, GetDocumentByIdDomainsData, GetDocumentByIdDomainsResponse, PutDocumentByIdDomainsData, PutDocumentByIdDomainsResponse, PutDocumentByIdMoveData, PutDocumentByIdMoveResponse, PutDocumentByIdMoveToRecycleBinData, PutDocumentByIdMoveToRecycleBinResponse, GetDocumentByIdNotificationsData, GetDocumentByIdNotificationsResponse, PutDocumentByIdNotificationsData, PutDocumentByIdNotificationsResponse, PostDocumentByIdPublicAccessData, PostDocumentByIdPublicAccessResponse, DeleteDocumentByIdPublicAccessData, DeleteDocumentByIdPublicAccessResponse, GetDocumentByIdPublicAccessData, GetDocumentByIdPublicAccessResponse, PutDocumentByIdPublicAccessData, PutDocumentByIdPublicAccessResponse, PutDocumentByIdPublishData, PutDocumentByIdPublishResponse, PutDocumentByIdPublishWithDescendantsData, PutDocumentByIdPublishWithDescendantsResponse, GetDocumentByIdReferencedByData, GetDocumentByIdReferencedByResponse, GetDocumentByIdReferencedDescendantsData, GetDocumentByIdReferencedDescendantsResponse, PutDocumentByIdUnpublishData, PutDocumentByIdUnpublishResponse, PutDocumentByIdValidateData, PutDocumentByIdValidateResponse, GetDocumentAreReferencedData, GetDocumentAreReferencedResponse, GetDocumentConfigurationResponse, PutDocumentSortData, PutDocumentSortResponse, GetDocumentUrlsData, GetDocumentUrlsResponse, PostDocumentValidateData, PostDocumentValidateResponse, GetItemDocumentData, GetItemDocumentResponse, GetItemDocumentSearchData, GetItemDocumentSearchResponse, DeleteRecycleBinDocumentResponse, DeleteRecycleBinDocumentByIdData, DeleteRecycleBinDocumentByIdResponse, GetRecycleBinDocumentByIdOriginalParentData, GetRecycleBinDocumentByIdOriginalParentResponse, PutRecycleBinDocumentByIdRestoreData, PutRecycleBinDocumentByIdRestoreResponse, GetRecycleBinDocumentChildrenData, GetRecycleBinDocumentChildrenResponse, GetRecycleBinDocumentRootData, GetRecycleBinDocumentRootResponse, GetTreeDocumentAncestorsData, GetTreeDocumentAncestorsResponse, GetTreeDocumentChildrenData, GetTreeDocumentChildrenResponse, GetTreeDocumentRootData, GetTreeDocumentRootResponse, PostDynamicRootQueryData, PostDynamicRootQueryResponse, GetDynamicRootStepsResponse, GetHealthCheckGroupData, GetHealthCheckGroupResponse, GetHealthCheckGroupByNameData, GetHealthCheckGroupByNameResponse, PostHealthCheckGroupByNameCheckData, PostHealthCheckGroupByNameCheckResponse, PostHealthCheckExecuteActionData, PostHealthCheckExecuteActionResponse, GetHelpData, GetHelpResponse, GetImagingResizeUrlsData, GetImagingResizeUrlsResponse, GetImportAnalyzeData, GetImportAnalyzeResponse, GetIndexerData, GetIndexerResponse, GetIndexerByIndexNameData, GetIndexerByIndexNameResponse, PostIndexerByIndexNameRebuildData, PostIndexerByIndexNameRebuildResponse, GetInstallSettingsResponse, PostInstallSetupData, PostInstallSetupResponse, PostInstallValidateDatabaseData, PostInstallValidateDatabaseResponse, GetItemLanguageData, GetItemLanguageResponse, GetItemLanguageDefaultResponse, GetLanguageData, GetLanguageResponse, PostLanguageData, PostLanguageResponse, GetLanguageByIsoCodeData, GetLanguageByIsoCodeResponse, DeleteLanguageByIsoCodeData, DeleteLanguageByIsoCodeResponse, PutLanguageByIsoCodeData, PutLanguageByIsoCodeResponse, GetLogViewerLevelData, GetLogViewerLevelResponse, GetLogViewerLevelCountData, GetLogViewerLevelCountResponse, GetLogViewerLogData, GetLogViewerLogResponse, GetLogViewerMessageTemplateData, GetLogViewerMessageTemplateResponse, GetLogViewerSavedSearchData, GetLogViewerSavedSearchResponse, PostLogViewerSavedSearchData, PostLogViewerSavedSearchResponse, GetLogViewerSavedSearchByNameData, GetLogViewerSavedSearchByNameResponse, DeleteLogViewerSavedSearchByNameData, DeleteLogViewerSavedSearchByNameResponse, GetLogViewerValidateLogsSizeData, GetLogViewerValidateLogsSizeResponse, GetManifestManifestResponse, GetManifestManifestPrivateResponse, GetManifestManifestPublicResponse, GetItemMediaTypeData, GetItemMediaTypeResponse, GetItemMediaTypeAllowedData, GetItemMediaTypeAllowedResponse, GetItemMediaTypeFoldersData, GetItemMediaTypeFoldersResponse, GetItemMediaTypeSearchData, GetItemMediaTypeSearchResponse, PostMediaTypeData, PostMediaTypeResponse, GetMediaTypeByIdData, GetMediaTypeByIdResponse, DeleteMediaTypeByIdData, DeleteMediaTypeByIdResponse, PutMediaTypeByIdData, PutMediaTypeByIdResponse, GetMediaTypeByIdAllowedChildrenData, GetMediaTypeByIdAllowedChildrenResponse, GetMediaTypeByIdCompositionReferencesData, GetMediaTypeByIdCompositionReferencesResponse, PostMediaTypeByIdCopyData, PostMediaTypeByIdCopyResponse, GetMediaTypeByIdExportData, GetMediaTypeByIdExportResponse, PutMediaTypeByIdImportData, PutMediaTypeByIdImportResponse, PutMediaTypeByIdMoveData, PutMediaTypeByIdMoveResponse, GetMediaTypeAllowedAtRootData, GetMediaTypeAllowedAtRootResponse, PostMediaTypeAvailableCompositionsData, PostMediaTypeAvailableCompositionsResponse, GetMediaTypeConfigurationResponse, PostMediaTypeFolderData, PostMediaTypeFolderResponse, GetMediaTypeFolderByIdData, GetMediaTypeFolderByIdResponse, DeleteMediaTypeFolderByIdData, DeleteMediaTypeFolderByIdResponse, PutMediaTypeFolderByIdData, PutMediaTypeFolderByIdResponse, PostMediaTypeImportData, PostMediaTypeImportResponse, GetTreeMediaTypeAncestorsData, GetTreeMediaTypeAncestorsResponse, GetTreeMediaTypeChildrenData, GetTreeMediaTypeChildrenResponse, GetTreeMediaTypeRootData, GetTreeMediaTypeRootResponse, GetCollectionMediaData, GetCollectionMediaResponse, GetItemMediaData, GetItemMediaResponse, GetItemMediaSearchData, GetItemMediaSearchResponse, PostMediaData, PostMediaResponse, GetMediaByIdData, GetMediaByIdResponse, DeleteMediaByIdData, DeleteMediaByIdResponse, PutMediaByIdData, PutMediaByIdResponse, GetMediaByIdAuditLogData, GetMediaByIdAuditLogResponse, PutMediaByIdMoveData, PutMediaByIdMoveResponse, PutMediaByIdMoveToRecycleBinData, PutMediaByIdMoveToRecycleBinResponse, GetMediaByIdReferencedByData, GetMediaByIdReferencedByResponse, GetMediaByIdReferencedDescendantsData, GetMediaByIdReferencedDescendantsResponse, PutMediaByIdValidateData, PutMediaByIdValidateResponse, GetMediaAreReferencedData, GetMediaAreReferencedResponse, GetMediaConfigurationResponse, PutMediaSortData, PutMediaSortResponse, GetMediaUrlsData, GetMediaUrlsResponse, PostMediaValidateData, PostMediaValidateResponse, DeleteRecycleBinMediaResponse, DeleteRecycleBinMediaByIdData, DeleteRecycleBinMediaByIdResponse, GetRecycleBinMediaByIdOriginalParentData, GetRecycleBinMediaByIdOriginalParentResponse, PutRecycleBinMediaByIdRestoreData, PutRecycleBinMediaByIdRestoreResponse, GetRecycleBinMediaChildrenData, GetRecycleBinMediaChildrenResponse, GetRecycleBinMediaRootData, GetRecycleBinMediaRootResponse, GetTreeMediaAncestorsData, GetTreeMediaAncestorsResponse, GetTreeMediaChildrenData, GetTreeMediaChildrenResponse, GetTreeMediaRootData, GetTreeMediaRootResponse, GetItemMemberGroupData, GetItemMemberGroupResponse, GetMemberGroupData, GetMemberGroupResponse, PostMemberGroupData, PostMemberGroupResponse, GetMemberGroupByIdData, GetMemberGroupByIdResponse, DeleteMemberGroupByIdData, DeleteMemberGroupByIdResponse, PutMemberGroupByIdData, PutMemberGroupByIdResponse, GetTreeMemberGroupRootData, GetTreeMemberGroupRootResponse, GetItemMemberTypeData, GetItemMemberTypeResponse, GetItemMemberTypeSearchData, GetItemMemberTypeSearchResponse, PostMemberTypeData, PostMemberTypeResponse, GetMemberTypeByIdData, GetMemberTypeByIdResponse, DeleteMemberTypeByIdData, DeleteMemberTypeByIdResponse, PutMemberTypeByIdData, PutMemberTypeByIdResponse, GetMemberTypeByIdCompositionReferencesData, GetMemberTypeByIdCompositionReferencesResponse, PostMemberTypeByIdCopyData, PostMemberTypeByIdCopyResponse, PostMemberTypeAvailableCompositionsData, PostMemberTypeAvailableCompositionsResponse, GetMemberTypeConfigurationResponse, GetTreeMemberTypeRootData, GetTreeMemberTypeRootResponse, GetFilterMemberData, GetFilterMemberResponse, GetItemMemberData, GetItemMemberResponse, GetItemMemberSearchData, GetItemMemberSearchResponse, PostMemberData, PostMemberResponse, GetMemberByIdData, GetMemberByIdResponse, DeleteMemberByIdData, DeleteMemberByIdResponse, PutMemberByIdData, PutMemberByIdResponse, PutMemberByIdValidateData, PutMemberByIdValidateResponse, GetMemberConfigurationResponse, PostMemberValidateData, PostMemberValidateResponse, PostModelsBuilderBuildResponse, GetModelsBuilderDashboardResponse, GetModelsBuilderStatusResponse, GetObjectTypesData, GetObjectTypesResponse, GetOembedQueryData, GetOembedQueryResponse, PostPackageByNameRunMigrationData, PostPackageByNameRunMigrationResponse, GetPackageConfigurationResponse, GetPackageCreatedData, GetPackageCreatedResponse, PostPackageCreatedData, PostPackageCreatedResponse, GetPackageCreatedByIdData, GetPackageCreatedByIdResponse, DeletePackageCreatedByIdData, DeletePackageCreatedByIdResponse, PutPackageCreatedByIdData, PutPackageCreatedByIdResponse, GetPackageCreatedByIdDownloadData, GetPackageCreatedByIdDownloadResponse, GetPackageMigrationStatusData, GetPackageMigrationStatusResponse, GetItemPartialViewData, GetItemPartialViewResponse, PostPartialViewData, PostPartialViewResponse, GetPartialViewByPathData, GetPartialViewByPathResponse, DeletePartialViewByPathData, DeletePartialViewByPathResponse, PutPartialViewByPathData, PutPartialViewByPathResponse, PutPartialViewByPathRenameData, PutPartialViewByPathRenameResponse, PostPartialViewFolderData, PostPartialViewFolderResponse, GetPartialViewFolderByPathData, GetPartialViewFolderByPathResponse, DeletePartialViewFolderByPathData, DeletePartialViewFolderByPathResponse, GetPartialViewSnippetData, GetPartialViewSnippetResponse, GetPartialViewSnippetByIdData, GetPartialViewSnippetByIdResponse, GetTreePartialViewAncestorsData, GetTreePartialViewAncestorsResponse, GetTreePartialViewChildrenData, GetTreePartialViewChildrenResponse, GetTreePartialViewRootData, GetTreePartialViewRootResponse, DeletePreviewResponse, PostPreviewResponse, GetProfilingStatusResponse, PutProfilingStatusData, PutProfilingStatusResponse, GetPropertyTypeIsUsedData, GetPropertyTypeIsUsedResponse, PostPublishedCacheCollectResponse, PostPublishedCacheRebuildResponse, PostPublishedCacheReloadResponse, GetPublishedCacheStatusResponse, GetRedirectManagementData, GetRedirectManagementResponse, GetRedirectManagementByIdData, GetRedirectManagementByIdResponse, DeleteRedirectManagementByIdData, DeleteRedirectManagementByIdResponse, GetRedirectManagementStatusResponse, PostRedirectManagementStatusData, PostRedirectManagementStatusResponse, GetItemRelationTypeData, GetItemRelationTypeResponse, GetRelationTypeData, GetRelationTypeResponse, GetRelationTypeByIdData, GetRelationTypeByIdResponse, GetRelationByRelationTypeIdData, GetRelationByRelationTypeIdResponse, GetItemScriptData, GetItemScriptResponse, PostScriptData, PostScriptResponse, GetScriptByPathData, GetScriptByPathResponse, DeleteScriptByPathData, DeleteScriptByPathResponse, PutScriptByPathData, PutScriptByPathResponse, PutScriptByPathRenameData, PutScriptByPathRenameResponse, PostScriptFolderData, PostScriptFolderResponse, GetScriptFolderByPathData, GetScriptFolderByPathResponse, DeleteScriptFolderByPathData, DeleteScriptFolderByPathResponse, GetTreeScriptAncestorsData, GetTreeScriptAncestorsResponse, GetTreeScriptChildrenData, GetTreeScriptChildrenResponse, GetTreeScriptRootData, GetTreeScriptRootResponse, GetSearcherData, GetSearcherResponse, GetSearcherBySearcherNameQueryData, GetSearcherBySearcherNameQueryResponse, GetSecurityConfigurationResponse, PostSecurityForgotPasswordData, PostSecurityForgotPasswordResponse, PostSecurityForgotPasswordResetData, PostSecurityForgotPasswordResetResponse, PostSecurityForgotPasswordVerifyData, PostSecurityForgotPasswordVerifyResponse, GetSegmentData, GetSegmentResponse, GetServerConfigurationResponse, GetServerInformationResponse, GetServerStatusResponse, GetServerTroubleshootingResponse, GetItemStaticFileData, GetItemStaticFileResponse, GetTreeStaticFileAncestorsData, GetTreeStaticFileAncestorsResponse, GetTreeStaticFileChildrenData, GetTreeStaticFileChildrenResponse, GetTreeStaticFileRootData, GetTreeStaticFileRootResponse, GetItemStylesheetData, GetItemStylesheetResponse, PostStylesheetData, PostStylesheetResponse, GetStylesheetByPathData, GetStylesheetByPathResponse, DeleteStylesheetByPathData, DeleteStylesheetByPathResponse, PutStylesheetByPathData, PutStylesheetByPathResponse, PutStylesheetByPathRenameData, PutStylesheetByPathRenameResponse, PostStylesheetFolderData, PostStylesheetFolderResponse, GetStylesheetFolderByPathData, GetStylesheetFolderByPathResponse, DeleteStylesheetFolderByPathData, DeleteStylesheetFolderByPathResponse, GetTreeStylesheetAncestorsData, GetTreeStylesheetAncestorsResponse, GetTreeStylesheetChildrenData, GetTreeStylesheetChildrenResponse, GetTreeStylesheetRootData, GetTreeStylesheetRootResponse, GetTagData, GetTagResponse, GetTelemetryData, GetTelemetryResponse, GetTelemetryLevelResponse, PostTelemetryLevelData, PostTelemetryLevelResponse, GetItemTemplateData, GetItemTemplateResponse, GetItemTemplateSearchData, GetItemTemplateSearchResponse, PostTemplateData, PostTemplateResponse, GetTemplateByIdData, GetTemplateByIdResponse, DeleteTemplateByIdData, DeleteTemplateByIdResponse, PutTemplateByIdData, PutTemplateByIdResponse, GetTemplateConfigurationResponse, PostTemplateQueryExecuteData, PostTemplateQueryExecuteResponse, GetTemplateQuerySettingsResponse, GetTreeTemplateAncestorsData, GetTreeTemplateAncestorsResponse, GetTreeTemplateChildrenData, GetTreeTemplateChildrenResponse, GetTreeTemplateRootData, GetTreeTemplateRootResponse, PostTemporaryFileData, PostTemporaryFileResponse, GetTemporaryFileByIdData, GetTemporaryFileByIdResponse, DeleteTemporaryFileByIdData, DeleteTemporaryFileByIdResponse, GetTemporaryFileConfigurationResponse, PostUpgradeAuthorizeResponse, GetUpgradeSettingsResponse, PostUserDataData, PostUserDataResponse, GetUserDataData, GetUserDataResponse, PutUserDataData, PutUserDataResponse, GetUserDataByIdData, GetUserDataByIdResponse, GetFilterUserGroupData, GetFilterUserGroupResponse, GetItemUserGroupData, GetItemUserGroupResponse, DeleteUserGroupData, DeleteUserGroupResponse, PostUserGroupData, PostUserGroupResponse, GetUserGroupData, GetUserGroupResponse, GetUserGroupByIdData, GetUserGroupByIdResponse, DeleteUserGroupByIdData, DeleteUserGroupByIdResponse, PutUserGroupByIdData, PutUserGroupByIdResponse, DeleteUserGroupByIdUsersData, DeleteUserGroupByIdUsersResponse, PostUserGroupByIdUsersData, PostUserGroupByIdUsersResponse, GetFilterUserData, GetFilterUserResponse, GetItemUserData, GetItemUserResponse, PostUserData, PostUserResponse, DeleteUserData, DeleteUserResponse, GetUserData, GetUserResponse, GetUserByIdData, GetUserByIdResponse, DeleteUserByIdData, DeleteUserByIdResponse, PutUserByIdData, PutUserByIdResponse, GetUserById2FaData, GetUserById2FaResponse, DeleteUserById2FaByProviderNameData, DeleteUserById2FaByProviderNameResponse, GetUserByIdCalculateStartNodesData, GetUserByIdCalculateStartNodesResponse, PostUserByIdChangePasswordData, PostUserByIdChangePasswordResponse, PostUserByIdResetPasswordData, PostUserByIdResetPasswordResponse, DeleteUserAvatarByIdData, DeleteUserAvatarByIdResponse, PostUserAvatarByIdData, PostUserAvatarByIdResponse, GetUserConfigurationResponse, GetUserCurrentResponse, GetUserCurrent2FaResponse, DeleteUserCurrent2FaByProviderNameData, DeleteUserCurrent2FaByProviderNameResponse, PostUserCurrent2FaByProviderNameData, PostUserCurrent2FaByProviderNameResponse, GetUserCurrent2FaByProviderNameData, GetUserCurrent2FaByProviderNameResponse, PostUserCurrentAvatarData, PostUserCurrentAvatarResponse, PostUserCurrentChangePasswordData, PostUserCurrentChangePasswordResponse, GetUserCurrentConfigurationResponse, GetUserCurrentLoginProvidersResponse, GetUserCurrentPermissionsData, GetUserCurrentPermissionsResponse, GetUserCurrentPermissionsDocumentData, GetUserCurrentPermissionsDocumentResponse, GetUserCurrentPermissionsMediaData, GetUserCurrentPermissionsMediaResponse, PostUserDisableData, PostUserDisableResponse, PostUserEnableData, PostUserEnableResponse, PostUserInviteData, PostUserInviteResponse, PostUserInviteCreatePasswordData, PostUserInviteCreatePasswordResponse, PostUserInviteResendData, PostUserInviteResendResponse, PostUserInviteVerifyData, PostUserInviteVerifyResponse, PostUserSetUserGroupsData, PostUserSetUserGroupsResponse, PostUserUnlockData, PostUserUnlockResponse, GetItemWebhookData, GetItemWebhookResponse, GetWebhookData, GetWebhookResponse, PostWebhookData, PostWebhookResponse, GetWebhookByIdData, GetWebhookByIdResponse, DeleteWebhookByIdData, DeleteWebhookByIdResponse, PutWebhookByIdData, PutWebhookByIdResponse, GetWebhookEventsData, GetWebhookEventsResponse, PostCurrentUserChangePasswordData, PostCurrentUserChangePasswordResponse } from './types.gen'; export class CultureService { /** @@ -8102,6 +8102,29 @@ export class UserService { } }); } + + /** + * @param data The data for the request. + * @param data.id + * @param data.requestBody + * @returns string OK + * @throws ApiError + */ + public static postCurrentUserByIdChangePassword(data: PostCurrentUserChangePasswordData): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/umbraco/management/api/v1/user/current/change-password', + body: data.requestBody, + mediaType: 'application/json', + responseHeader: 'Umb-Notifications', + errors: { + 400: 'Bad Request', + 401: 'The resource is protected and requires an authentication token', + 403: 'The authenticated user do not have access to this resource', + 404: 'Not Found' + } + }); + } /** * @param data The data for the request. diff --git a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/types.gen.ts b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/types.gen.ts index 2d29b8b87e..ffc97171b1 100644 --- a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/types.gen.ts +++ b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/types.gen.ts @@ -5068,6 +5068,12 @@ export type PostUserByIdChangePasswordData = { export type PostUserByIdChangePasswordResponse = string; +export type PostCurrentUserChangePasswordData = { + requestBody?: ChangePasswordCurrentUserRequestModel; +}; + +export type PostCurrentUserChangePasswordResponse = string; + export type PostUserByIdResetPasswordData = { id: string; }; diff --git a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.controller.ts index 8a29e63be5..f4e1b9d4fc 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/extension-api/controller/base-extensions-initializer.controller.ts @@ -111,8 +111,6 @@ export abstract class UmbBaseExtensionsInitializer< manifests.forEach((manifest) => { const existing = this._extensions.find((x) => x.alias === manifest.alias); if (!existing) { - // Idea: could be abstracted into a createController method, so we can override it in a subclass. - // (This should be enough to be able to create a element extension controller instead.) this._extensions.push(this._createController(manifest)); } }); diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts index 7aa782b8a2..f3c0ab8d4d 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts @@ -406,6 +406,27 @@ export const data: Array = [ }, ], }, + { + name: 'Dropdown Alignment Options', + id: 'dt-dropdown-align', + parent: null, + editorAlias: 'Umbraco.DropDown.Flexible', + editorUiAlias: 'Umb.PropertyEditorUi.Dropdown', + hasChildren: false, + isFolder: false, + isDeletable: true, + canIgnoreStartNodes: false, + values: [ + { + alias: 'multiple', + value: false, + }, + { + alias: 'items', + value: ['left', 'center', 'right'], + }, + ], + }, { name: 'Slider', id: 'dt-slider', @@ -587,6 +608,7 @@ export const data: Array = [ { label: 'Headline', contentElementTypeKey: 'headline-umbraco-demo-block-id', + settingsElementTypeKey: 'headline-settings-demo-block-id', backgroundColor: 'gold', editorSize: 'medium', icon: 'icon-edit', @@ -613,7 +635,7 @@ export const data: Array = [ }, { alias: 'useInlineEditingAsDefault', - value: true, + value: false, }, { alias: 'useLiveEditing', diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts index e8ef422e04..1011e3c467 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts @@ -1507,6 +1507,60 @@ export const data: Array = [ }, ], }, + { + allowedTemplates: [], + defaultTemplate: null, + id: 'headline-settings-demo-block-id', + alias: 'headlineSettingsUmbracoDemoBlock', + name: 'Headline', + description: null, + icon: 'icon-edit', + allowedAsRoot: true, + variesByCulture: false, + variesBySegment: false, + isElement: true, + hasChildren: false, + parent: { id: 'folder-umbraco-demo-blocks-id' }, + isFolder: false, + allowedDocumentTypes: [], + compositions: [], + cleanup: { + preventCleanup: false, + keepAllVersionsNewerThanDays: null, + keepLatestVersionPerDayForDays: null, + }, + properties: [ + { + id: 'block-alignment-id', + container: { id: 'settings-group-key' }, + alias: 'blockAlignment', + name: 'Block Alignment', + description: '', + dataType: { id: 'dt-dropdown-align' }, + variesByCulture: false, + variesBySegment: false, + sortOrder: 0, + validation: { + mandatory: false, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + ], + containers: [ + { + id: 'settings-group-key', + parent: null, + name: 'Settings', + type: 'Group', + sortOrder: 0, + }, + ], + }, { allowedTemplates: [], defaultTemplate: null, diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/views/settings.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/views/settings.element.ts index cfbc9bd694..b8043eba6f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/views/settings.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/views/settings.element.ts @@ -1,15 +1,40 @@ -import { css, html, customElement } from '@umbraco-cms/backoffice/external/lit'; +import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry'; +import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property'; +import type { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; +import type { UmbInputNumberRangeElement } from '@umbraco-cms/backoffice/components'; @customElement('umb-block-grid-area-type-workspace-view') export class UmbBlockGridAreaTypeWorkspaceViewSettingsElement extends UmbLitElement implements UmbWorkspaceViewElement { // TODO: Add Localizations... // TODO: Validation to prevent spaces and weird characters in alias: - // TODO: Add create button label field: - // TODO: Turn minAllowed and maxAllowed into one range property/input... - // TODO: Add validation permission field: + + #dataset?: typeof UMB_PROPERTY_DATASET_CONTEXT.TYPE; + + @state() + _minValue?: number; + @state() + _maxValue?: number; + + constructor() { + super(); + this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, async (context) => { + this.#dataset = context; + this.observe(await this.#dataset.propertyValueByAlias('minAllowed'), (min) => { + this._minValue = min ?? 0; + }); + this.observe(await this.#dataset.propertyValueByAlias('maxAllowed'), (max) => { + this._maxValue = max ?? Infinity; + }); + }); + } + #onAllowedRangeChange = (e: UmbChangeEvent) => { + this.#dataset?.setPropertyValue('minAllowed', (e!.target! as UmbInputNumberRangeElement).minValue); + this.#dataset?.setPropertyValue('maxAllowed', (e!.target! as UmbInputNumberRangeElement).maxValue); + }; + override render() { return html` @@ -24,14 +49,14 @@ export class UmbBlockGridAreaTypeWorkspaceViewSettingsElement extends UmbLitElem property-editor-ui-alias="Umb.PropertyEditorUi.TextBox"> - - + + + + { @@ -207,6 +220,14 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen null, ); + this.observe( + this.#context.hasTypeLimits, + (hasTypeLimits) => { + this.#setupBlockTypeLimitValidation(hasTypeLimits); + }, + null, + ); + this.#context.getManager().then((manager) => { this.observe( manager.layoutStylesheet, @@ -223,8 +244,6 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen new UmbFormControlValidator(this, this /*, this.#dataPath*/); } - #rangeUnderflowValidator?: UmbFormControlValidatorConfig; - #rangeOverflowValidator?: UmbFormControlValidatorConfig; async #setupRangeValidation(rangeLimit: UmbNumberRangeValueType | undefined) { if (this.#rangeUnderflowValidator) { this.removeValidator(this.#rangeUnderflowValidator); @@ -240,9 +259,7 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen (rangeLimit!.min ?? 0) - this._layoutEntries.length, ); }, - () => { - return this._layoutEntries.length < (rangeLimit?.min ?? 0); - }, + () => this._layoutEntries.length < (rangeLimit?.min ?? 0), ); } @@ -260,8 +277,37 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen this._layoutEntries.length - (rangeLimit!.max ?? this._layoutEntries.length), ); }, + () => this._layoutEntries.length > (rangeLimit?.max ?? Infinity), + ); + } + } + + async #setupBlockTypeLimitValidation(hasTypeLimits: boolean | undefined) { + if (this.#typeLimitValidator) { + this.removeValidator(this.#typeLimitValidator); + this.#typeLimitValidator = undefined; + } + if (hasTypeLimits) { + this.#typeLimitValidator = this.addValidator( + 'customError', () => { - return (this._layoutEntries.length ?? 0) > (rangeLimit?.max ?? Infinity); + const invalids = this.#context.getInvalidBlockTypeLimits(); + return invalids + .map((invalidRule) => + this.localize.term( + invalidRule.amount < invalidRule.minRequirement + ? 'blockEditor_areaValidationEntriesShort' + : 'blockEditor_areaValidationEntriesExceed', + invalidRule.name, + invalidRule.amount, + invalidRule.minRequirement, + invalidRule.maxRequirement, + ), + ) + .join(', '); + }, + () => { + return !this.#context.checkBlockTypeLimitsValidity(); }, ); } @@ -284,14 +330,14 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen `, )}
- ${this._canCreate ? this.#renderCreateButton() : nothing} + ${this._areaKey ? html` ` : nothing} `; } #renderCreateButton() { if (this._areaKey === null || this._layoutEntries.length === 0) { - return html` + return html` div { display: flex; flex-direction: column; align-items: stretch; } - uui-button-group { + #createButton { padding-top: 1px; grid-template-columns: 1fr auto; + display: grid; + } + // Only when we are n an area, we like to hide the button on drag + :host([area-key]) #createButton { --umb-block-grid--is-dragging--variable: var(--umb-block-grid--is-dragging) none; display: var(--umb-block-grid--is-dragging--variable, grid); } + :host(:not([pristine]):invalid) #createButton { + --uui-button-contrast: var(--uui-color-danger); + --uui-button-contrast-hover: var(--uui-color-danger); + --uui-color-default-emphasis: var(--uui-color-danger); + --uui-button-border-color: var(--uui-color-danger); + --uui-button-border-color-hover: var(--uui-color-danger); + } .umb-block-grid__layout-container[data-area-length='0'] { --umb-block-grid--is-dragging--variable: var(--umb-block-grid--is-dragging) 1; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts index 4778221731..291e9f269d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts @@ -13,6 +13,10 @@ import { UMB_BLOCK_GRID, type UmbBlockGridLayoutModel } from '@umbraco-cms/backo import '../block-grid-block-inline/index.js'; import '../block-grid-block/index.js'; import '../block-scale-handler/index.js'; +import { UmbObserveValidationStateController } from '@umbraco-cms/backoffice/validation'; +import { UmbDataPathBlockElementDataQuery } from '@umbraco-cms/backoffice/block'; +import { UUIBlinkAnimationValue, UUIBlinkKeyframes } from '@umbraco-cms/backoffice/external/uui'; +import type { UmbExtensionElementInitializer } from '@umbraco-cms/backoffice/extension-api'; /** * @element umb-block-grid-entry */ @@ -37,6 +41,16 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper this._blockViewProps.contentUdi = value; this.setAttribute('data-element-udi', value); this.#context.setContentUdi(value); + + new UmbObserveValidationStateController( + this, + `$.contentData[${UmbDataPathBlockElementDataQuery({ udi: value })}]`, + (hasMessages) => { + this._contentInvalid = hasMessages; + this._blockViewProps.contentInvalid = hasMessages; + }, + 'observeMessagesForContent', + ); } private _contentUdi?: string | undefined; // @@ -89,6 +103,14 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper @state() _inlineCreateAboveWidth?: string; + // 'content-invalid' attribute is used for styling purpose. + @property({ type: Boolean, attribute: 'content-invalid', reflect: true }) + _contentInvalid?: boolean; + + // 'settings-invalid' attribute is used for styling purpose. + @property({ type: Boolean, attribute: 'settings-invalid', reflect: true }) + _settingsInvalid?: boolean; + @state() _blockViewProps: UmbBlockEditorCustomViewProperties = { contentUdi: undefined!, @@ -178,6 +200,20 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper this.#context.settings, (settings) => { this.#updateBlockViewProps({ settings }); + + this.removeUmbControllerByAlias('observeMessagesForSettings'); + if (settings) { + // Observe settings validation state: + new UmbObserveValidationStateController( + this, + `$.settingsData[${UmbDataPathBlockElementDataQuery(settings)}]`, + (hasMessages) => { + this._settingsInvalid = hasMessages; + this._blockViewProps.settingsInvalid = hasMessages; + }, + 'observeMessagesForSettings', + ); + } }, null, ); @@ -318,12 +354,23 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper return true; }; + #extensionSlotRenderMethod = (ext: UmbExtensionElementInitializer) => { + if (ext.component) { + ext.component.classList.add('umb-block-grid__block--view'); + } + return ext.component; + }; + #renderInlineEditBlock() { - return html``; + return html``; } #renderRefBlock() { - return html``; + return html``; } #renderBlock() { @@ -339,28 +386,47 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper : nothing}
${this._inlineEditingMode ? this.#renderInlineEditBlock() : this.#renderRefBlock()} ${this._showContentEdit && this._workspaceEditContentPath - ? html` + ? html` + ${this._contentInvalid + ? html`!` + : nothing} ` : nothing} ${this._hasSettings && this._workspaceEditSettingsPath - ? html` + ? html` + ${this._settingsInvalid + ? html`!` + : nothing} ` : nothing} - this.#context.requestDelete()}> + this.#context.requestDelete()}> + ${!this._showContentEdit && this._contentInvalid + ? html`!` + : nothing} ${this._canScale ? html` this.#context.scaleManager.onScaleMouseDown(e)}> @@ -383,21 +449,31 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper } static override styles = [ + UUIBlinkKeyframes, css` :host { position: relative; display: block; + --umb-block-grid-entry-actions-opacity: 0; } + :host([settings-invalid]), + :host([content-invalid]), + :host(:hover), + :host(:focus-within) { + --umb-block-grid-entry-actions-opacity: 1; + } + uui-action-bar { position: absolute; top: var(--uui-size-2); right: var(--uui-size-2); + opacity: var(--umb-block-grid-entry-actions-opacity, 0); + transition: opacity 120ms; } uui-button-inline-create { top: 0px; position: absolute; - // Avoid showing inline-create in dragging-mode --umb-block-grid__block--inline-create-button-display--condition: var(--umb-block-grid--dragging-mode) none; display: var(--umb-block-grid__block--inline-create-button-display--condition); } @@ -412,35 +488,60 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper right: calc(1px - (var(--umb-block-grid--column-gap, 0px) * 0.5)); } - :host([drag-placeholder]) { - opacity: 0.2; + .umb-block-grid__block { + height: 100%; } - :host(::after) { + :host::after { content: ''; position: absolute; z-index: 1; pointer-events: none; - display: none; inset: 0; border: 1px solid transparent; - border-radius: 3px; + border-radius: var(--uui-border-radius); box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.7), inset 0 0 0 1px rgba(255, 255, 255, 0.7); transition: border-color 240ms ease-in; } - - :host(:hover::after) { - // TODO: Look at the feature I out-commented here, what was that suppose to do [NL]: - //display: var(--umb-block-grid--block-ui-display, block); + :host(:hover):not(:drop)::after { display: block; - border-color: var(--uui-color-interactive); + border-color: var(--uui-color-interactive-emphasis); } - .umb-block-grid__block { - height: 100%; + :host([drag-placeholder])::after { + display: block; + border-width: 2px; + border-color: var(--uui-color-interactive-emphasis); + animation: ${UUIBlinkAnimationValue}; + } + :host([drag-placeholder])::before { + content: ''; + position: absolute; + pointer-events: none; + inset: 0; + border-radius: var(--uui-border-radius); + background-color: var(--uui-color-interactive-emphasis); + opacity: 0.12; + } + :host([drag-placeholder]) .umb-block-grid__block { + transition: opacity 50ms 16ms; + opacity: 0; + } + + :host([settings-invalid])::after, + :host([content-invalid])::after { + border-color: var(--uui-color-danger); + } + :host([settings-invalid])::before, + :host([content-invalid])::before { + background-color: var(--uui-color-danger); + } + + uui-badge { + z-index: 2; } `, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts index b40bf030ed..ebbec1134f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts @@ -8,7 +8,13 @@ import { import type { UmbBlockGridLayoutModel, UmbBlockGridTypeAreaType, UmbBlockGridTypeModel } from '../types.js'; import { UMB_BLOCK_GRID_MANAGER_CONTEXT } from './block-grid-manager.context-token.js'; import type { UmbBlockGridScalableContainerContext } from './block-grid-scale-manager/block-grid-scale-manager.controller.js'; -import { UmbArrayState, UmbNumberState, UmbObjectState, UmbStringState } from '@umbraco-cms/backoffice/observable-api'; +import { + UmbArrayState, + UmbBooleanState, + UmbNumberState, + UmbObjectState, + UmbStringState, +} from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router'; import { pathFolderName } from '@umbraco-cms/backoffice/utils'; @@ -49,6 +55,9 @@ export class UmbBlockGridEntriesContext public readonly amountOfAllowedBlockTypes = this.#allowedBlockTypes.asObservablePart((x) => x.length); public readonly canCreate = this.#allowedBlockTypes.asObservablePart((x) => x.length > 0); + #hasTypeLimits = new UmbBooleanState(undefined); + public readonly hasTypeLimits = this.#hasTypeLimits.asObservable(); + firstAllowedBlockTypeName() { if (!this._manager) { throw new Error('Manager not ready'); @@ -84,6 +93,10 @@ export class UmbBlockGridEntriesContext this.#workspaceModal.setUniquePathValue('areaKey', areaKey ?? 'null'); this.#catalogueModal.setUniquePathValue('areaKey', areaKey ?? 'null'); this.#gotAreaKey(); + + // Idea: If we need to parse down a validation data path to target the specific layout object: [NL] + // If we have a areaKey, we want to inherit our layoutDataPath from nearest blockGridEntry context. + // If not, we want to set the layoutDataPath to a base one. } setLayoutColumns(columns: number | undefined) { @@ -133,9 +146,30 @@ export class UmbBlockGridEntriesContext blockGroups: this._manager?.getBlockGroups() ?? [], openClipboard: routingInfo.view === 'clipboard', originData: { index: index, areaKey: this.#areaKey, parentUnique: this.#parentUnique }, + createBlockInWorkspace: true, }, }; }) + .onSubmit(async (value, data) => { + if (value?.create && data) { + const created = await this.create( + value.create.contentElementTypeKey, + // We can parse an empty object, cause the rest will be filled in by others. + {} as any, + data.originData as UmbBlockGridWorkspaceOriginData, + ); + if (created) { + this.insert( + created.layout, + created.content, + created.settings, + data.originData as UmbBlockGridWorkspaceOriginData, + ); + } else { + throw new Error('Failed to create block'); + } + } + }) .observeRouteBuilder((routeBuilder) => { // TODO: Does it make any sense that this is a state? Check usage and confirm. [NL] this._catalogueRouteBuilderState.setValue(routeBuilder); @@ -163,8 +197,8 @@ export class UmbBlockGridEntriesContext protected _gotBlockManager() { if (!this._manager) return; - this.#getAllowedBlockTypes(); - this.#getRangeLimits(); + this.#setupAllowedBlockTypes(); + this.#setupRangeLimits(); this.observe( this._manager.propertyAlias, @@ -216,8 +250,6 @@ export class UmbBlockGridEntriesContext 'observeThisLayouts', ); - this.removeUmbControllerByAlias('observeAreaType'); - const hostEl = this.getHostElement() as HTMLElement | undefined; if (hostEl) { hostEl.removeAttribute('data-area-alias'); @@ -229,8 +261,8 @@ export class UmbBlockGridEntriesContext } this.removeUmbControllerByAlias('observeAreaType'); - this.#getAllowedBlockTypes(); - this.#getRangeLimits(); + this.#setupAllowedBlockTypes(); + this.#setupRangeLimits(); } else { if (!this.#parentEntry) return; @@ -273,22 +305,44 @@ export class UmbBlockGridEntriesContext hostEl.style.setProperty('--umb-block-grid--grid-columns', areaType?.columnSpan?.toString() ?? ''); hostEl.style.setProperty('--umb-block-grid--area-column-span', areaType?.columnSpan?.toString() ?? ''); hostEl.style.setProperty('--umb-block-grid--area-row-span', areaType?.rowSpan?.toString() ?? ''); - this.#getAllowedBlockTypes(); - this.#getRangeLimits(); + this.#setupAllowedBlockTypes(); + this.#setupRangeLimits(); }, 'observeAreaType', ); } } - #getAllowedBlockTypes() { + #setupAllowedBlockTypes() { if (!this._manager) return; this.#allowedBlockTypes.setValue(this.#retrieveAllowedElementTypes()); + this.#setupAllowedBlockTypesLimits(); } - #getRangeLimits() { + #setupRangeLimits() { if (!this._manager) return; - const range = this.#retrieveRangeLimits(); - this.#rangeLimits.setValue(range); + //const range = this.#retrieveRangeLimits(); + if (this.#areaKey != null) { + this.removeUmbControllerByAlias('observeConfigurationRootLimits'); + // Area entries: + if (!this.#areaType) return undefined; + // No need to observe as this method is called every time the area is changed. + this.#rangeLimits.setValue({ + min: this.#areaType.minAllowed ?? 0, + max: this.#areaType.maxAllowed ?? Infinity, + }); + } else if (this.#areaKey === null) { + if (!this._manager) return undefined; + + this.observe( + this._manager.editorConfiguration, + (config) => { + const min = config?.getValueByAlias('validationLimit')?.min ?? 0; + const max = config?.getValueByAlias('validationLimit')?.max ?? Infinity; + this.#rangeLimits.setValue({ min, max }); + }, + 'observeConfigurationRootLimits', + ); + } } getPathForCreateBlock(index: number) { @@ -385,24 +439,95 @@ export class UmbBlockGridEntriesContext /** * @internal - * @returns an NumberRange of the min and max allowed items in the current area. Or undefined if not ready jet. */ - #retrieveRangeLimits(): UmbNumberRangeValueType | undefined { - if (this.#areaKey != null) { + #setupAllowedBlockTypesLimits() { + if (!this._manager) return; + + if (this.#areaKey) { // Area entries: - if (!this.#areaType) return undefined; + if (!this.#areaType) return; - return { min: this.#areaType.minAllowed ?? 0, max: this.#areaType.maxAllowed ?? Infinity }; + if (this.#areaType.specifiedAllowance && this.#areaType.specifiedAllowance?.length > 0) { + this.#hasTypeLimits.setValue(true); + } } else if (this.#areaKey === null) { - if (!this._manager) return undefined; - - const config = this._manager.getEditorConfiguration(); - const min = config?.getValueByAlias('validationLimit')?.min ?? 0; - const max = config?.getValueByAlias('validationLimit')?.max ?? Infinity; - return { min, max }; + // RESET } + } - return undefined; + #invalidBlockTypeLimits?: Array<{ + groupKey?: string; + key?: string; + name: string; + amount: number; + minRequirement: number; + maxRequirement: number; + }>; + + getInvalidBlockTypeLimits() { + return this.#invalidBlockTypeLimits ?? []; + } + /** + * @internal + * @returns {boolean} - True if the block type limits are valid, otherwise false. + */ + checkBlockTypeLimitsValidity(): boolean { + if (!this.#areaType || !this.#areaType.specifiedAllowance) return false; + + const layoutEntries = this._layoutEntries.getValue(); + + this.#invalidBlockTypeLimits = []; + + const hasInvalidRules = this.#areaType.specifiedAllowance.some((rule) => { + const minAllowed = rule.minAllowed || 0; + const maxAllowed = rule.maxAllowed || 0; + + // For block groups: + if (rule.groupKey) { + const groupElementTypeKeys = + this._manager + ?.getBlockTypes() + .filter((blockType) => blockType.groupKey === rule.groupKey && blockType.allowInAreas === true) + .map((x) => x.contentElementTypeKey) ?? []; + const groupAmount = layoutEntries.filter((entry) => { + const contentTypeKey = this._manager!.getContentTypeKeyOf(entry.contentUdi); + return contentTypeKey ? groupElementTypeKeys.indexOf(contentTypeKey) !== -1 : false; + }).length; + + if (groupAmount < minAllowed || (maxAllowed > 0 && groupAmount > maxAllowed)) { + this.#invalidBlockTypeLimits!.push({ + groupKey: rule.groupKey, + name: this._manager!.getBlockGroupName(rule.groupKey) ?? '?', + amount: groupAmount, + minRequirement: minAllowed, + maxRequirement: maxAllowed, + }); + return true; + } + } + // For specific elementTypes: + else if (rule.elementTypeKey) { + const amount = layoutEntries.filter((entry) => { + const contentTypeKey = this._manager!.getContentOf(entry.contentUdi)?.contentTypeKey; + return contentTypeKey === rule.elementTypeKey; + }).length; + if (amount < minAllowed || (maxAllowed > 0 ? amount > maxAllowed : false)) { + this.#invalidBlockTypeLimits!.push({ + key: rule.elementTypeKey, + name: this._manager!.getContentTypeNameOf(rule.elementTypeKey) ?? '?', + amount: amount, + minRequirement: minAllowed, + maxRequirement: maxAllowed, + }); + return true; + } + } + + // Lets fail cause the rule was bad. + console.error('Invalid block type limit rule.', rule); + return false; + }); + return hasInvalidRules === false; } /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts index 8b473a66ce..416ce658ba 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts @@ -62,6 +62,9 @@ export class UmbBlockGridManagerContext< getBlockGroups() { return this.#blockGroups.value; } + getBlockGroupName(unique: string) { + return this.#blockGroups.getValue().find((group) => group.key === unique)?.name; + } constructor(host: UmbControllerHost) { super(host); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid.element.ts index 2f89ee2787..0c597e5ded 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid.element.ts @@ -1,16 +1,25 @@ +import { UmbBlockGridManagerContext } from '../../context/block-grid-manager.context.js'; +import { UMB_BLOCK_GRID_PROPERTY_EDITOR_ALIAS } from './manifests.js'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { html, customElement, property, state, css, type PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; +import { + html, + customElement, + property, + state, + css, + type PropertyValueMap, + ref, +} from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import '../../components/block-grid-entries/index.js'; import { observeMultiple } from '@umbraco-cms/backoffice/observable-api'; import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property'; -import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; -import { UmbBlockGridManagerContext } from '../../context/block-grid-manager.context.js'; -import { UMB_BLOCK_GRID_PROPERTY_EDITOR_ALIAS } from './manifests.js'; +import { UmbFormControlMixin, UmbValidationContext } from '@umbraco-cms/backoffice/validation'; import type { UmbBlockTypeGroup } from '@umbraco-cms/backoffice/block-type'; import type { UmbBlockGridTypeModel, UmbBlockGridValueModel } from '@umbraco-cms/backoffice/block-grid'; +import { UmbBlockElementDataValidationPathTranslator } from '@umbraco-cms/backoffice/block'; /** * @element umb-property-editor-ui-block-grid @@ -20,6 +29,9 @@ export class UmbPropertyEditorUIBlockGridElement extends UmbFormControlMixin(UmbLitElement) implements UmbPropertyEditorUiElement { + #validationContext = new UmbValidationContext(this).provide(); + #contentDataPathTranslator?: UmbBlockElementDataValidationPathTranslator; + #settingsDataPathTranslator?: UmbBlockElementDataValidationPathTranslator; #context = new UmbBlockGridManagerContext(this); // private _value: UmbBlockGridValueModel = { @@ -31,11 +43,6 @@ export class UmbPropertyEditorUIBlockGridElement public set config(config: UmbPropertyEditorConfigCollection | undefined) { if (!config) return; - /*const validationLimit = config.getValueByAlias('validationLimit'); - - this.#limitMin = validationLimit?.min; - this.#limitMax = validationLimit?.max;*/ - const blocks = config.getValueByAlias>('blocks') ?? []; this.#context.setBlockTypes(blocks); @@ -44,7 +51,7 @@ export class UmbPropertyEditorUIBlockGridElement this.style.maxWidth = config.getValueByAlias('maxPropertyWidth') ?? ''; - //config.useLiveEditing, is covered by the EditorConfiguration of context. + //config.useLiveEditing, is covered by the EditorConfiguration of context. [NL] this.#context.setEditorConfiguration(config); } @@ -70,6 +77,25 @@ export class UmbPropertyEditorUIBlockGridElement constructor() { super(); + this.consumeContext(UMB_PROPERTY_CONTEXT, (context) => { + this.observe( + context.dataPath, + (dataPath) => { + // Translate paths for content/settings: + this.#contentDataPathTranslator?.destroy(); + this.#settingsDataPathTranslator?.destroy(); + if (dataPath) { + // Set the data path for the local validation context: + this.#validationContext.setDataPath(dataPath); + + this.#contentDataPathTranslator = new UmbBlockElementDataValidationPathTranslator(this, 'contentData'); + this.#settingsDataPathTranslator = new UmbBlockElementDataValidationPathTranslator(this, 'settingsData'); + } + }, + 'observeDataPath', + ); + }); + // TODO: Prevent initial notification from these observes this.consumeContext(UMB_PROPERTY_CONTEXT, (propertyContext) => { this.observe( @@ -99,8 +125,21 @@ export class UmbPropertyEditorUIBlockGridElement }); } + #currentEntriesElement?: Element; + #gotRootEntriesElement(element: Element | undefined): void { + if (this.#currentEntriesElement === element) return; + if (this.#currentEntriesElement) { + this.removeFormControlElement(this.#currentEntriesElement as any); + } + this.#currentEntriesElement = element; + if (element) { + this.addFormControlElement(element as any); + } + } + override render() { return html` `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts index 6433e33864..4beee2607a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts @@ -248,6 +248,7 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper default-element=${this._inlineEditingMode ? 'umb-inline-list-block' : 'umb-ref-list-block'} .props=${this._blockViewProps} .filter=${this.#extensionSlotFilterMethod} + single >${this._inlineEditingMode ? this.#renderInlineBlock() : this.#renderRefBlock()} diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts index 9e263cf849..2d60a0d689 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts @@ -139,21 +139,14 @@ export class UmbPropertyEditorUIBlockListElement this.observe( context.dataPath, (dataPath) => { - // Translate paths for content elements: + // Translate paths for content/settings: this.#contentDataPathTranslator?.destroy(); - if (dataPath) { - // Set the data path for the local validation context: - this.#validationContext.setDataPath(dataPath); - - this.#contentDataPathTranslator = new UmbBlockElementDataValidationPathTranslator(this, 'contentData'); - } - - // Translate paths for settings elements: this.#settingsDataPathTranslator?.destroy(); if (dataPath) { // Set the data path for the local validation context: this.#validationContext.setDataPath(dataPath); + this.#contentDataPathTranslator = new UmbBlockElementDataValidationPathTranslator(this, 'contentData'); this.#settingsDataPathTranslator = new UmbBlockElementDataValidationPathTranslator(this, 'settingsData'); } }, @@ -163,13 +156,13 @@ export class UmbPropertyEditorUIBlockListElement this.addValidator( 'rangeUnderflow', - () => this.localize.term('validation_entriesShort'), + () => '#validation_entriesShort', () => !!this._limitMin && this.#entriesContext.getLength() < this._limitMin, ); this.addValidator( 'rangeOverflow', - () => this.localize.term('validation_entriesExceed'), + () => '#validation_entriesExceed', () => !!this._limitMax && this.#entriesContext.getLength() > this._limitMax, ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts index 8836730be1..3ca6afa49d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts @@ -151,6 +151,7 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert type="blockEditorCustomView" default-element=${'umb-ref-rte-block'} .props=${this._blockViewProps} + single >${this.#renderRefBlock()} diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-entries.context.ts index 41acf53cb6..ac2501c80a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-entries.context.ts @@ -4,7 +4,6 @@ import type { UmbBlockRteLayoutModel, UmbBlockRteTypeModel } from '../types.js'; import { UMB_BLOCK_RTE_WORKSPACE_MODAL, type UmbBlockRteWorkspaceOriginData, - type UmbBlockRteWorkspaceData, } from '../workspace/block-rte-workspace.modal-token.js'; import { UMB_BLOCK_RTE_MANAGER_CONTEXT } from './block-rte-manager.context-token.js'; import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api'; @@ -41,9 +40,30 @@ export class UmbBlockRteEntriesContext extends UmbBlockEntriesContext< blockGroups: [], openClipboard: routingInfo.view === 'clipboard', originData: {}, + createBlockInWorkspace: true, }, }; }) + .onSubmit(async (value, data) => { + if (value?.create && data) { + const created = await this.create( + value.create.contentElementTypeKey, + // We can parse an empty object, cause the rest will be filled in by others. + {} as any, + data.originData as UmbBlockRteWorkspaceOriginData, + ); + if (created) { + this.insert( + created.layout, + created.content, + created.settings, + data.originData as UmbBlockRteWorkspaceOriginData, + ); + } else { + throw new Error('Failed to create block'); + } + } + }) .observeRouteBuilder((routeBuilder) => { this._catalogueRouteBuilderState.setValue(routeBuilder); }); @@ -114,10 +134,10 @@ export class UmbBlockRteEntriesContext extends UmbBlockEntriesContext< async create( contentElementTypeKey: string, partialLayoutEntry?: Omit, - modalData?: UmbBlockRteWorkspaceData, + originData?: UmbBlockRteWorkspaceOriginData, ) { await this._retrieveManager; - return this._manager?.create(contentElementTypeKey, partialLayoutEntry, modalData); + return this._manager?.create(contentElementTypeKey, partialLayoutEntry, originData); } // insert Block? @@ -126,10 +146,10 @@ export class UmbBlockRteEntriesContext extends UmbBlockEntriesContext< layoutEntry: UmbBlockRteLayoutModel, content: UmbBlockDataType, settings: UmbBlockDataType | undefined, - modalData: UmbBlockRteWorkspaceData, + originData: UmbBlockRteWorkspaceOriginData, ) { await this._retrieveManager; - return this._manager?.insert(layoutEntry, content, settings, modalData) ?? false; + return this._manager?.insert(layoutEntry, content, settings, originData) ?? false; } // create Block? diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-manager.context.ts index ae67db7ac0..8f7e86d2a9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-manager.context.ts @@ -1,5 +1,5 @@ import type { UmbBlockRteLayoutModel, UmbBlockRteTypeModel } from '../types.js'; -import type { UmbBlockRteWorkspaceData } from '../index.js'; +import type { UmbBlockRteWorkspaceOriginData } from '../index.js'; import type { UmbBlockDataType } from '../../block/types.js'; import type { Editor } from '@umbraco-cms/backoffice/external/tinymce'; import { UmbBlockManagerContext } from '@umbraco-cms/backoffice/block'; @@ -36,7 +36,7 @@ export class UmbBlockRteManagerContext< partialLayoutEntry?: Omit, // This property is used by some implementations, but not used in this. // eslint-disable-next-line @typescript-eslint/no-unused-vars - originData?: UmbBlockRteWorkspaceData, + originData?: UmbBlockRteWorkspaceOriginData, ) { const data = super.createBlockData(contentElementTypeKey, partialLayoutEntry); @@ -57,13 +57,13 @@ export class UmbBlockRteManagerContext< layoutEntry: BlockLayoutType, content: UmbBlockDataType, settings: UmbBlockDataType | undefined, - modalData: UmbBlockRteWorkspaceData, + originData: UmbBlockRteWorkspaceOriginData, ) { if (!this.#editor) return false; this._layouts.appendOne(layoutEntry); - this.insertBlockData(layoutEntry, content, settings, modalData); + this.insertBlockData(layoutEntry, content, settings, originData); if (layoutEntry.displayInline) { this.#editor.selection.setContent( diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts index 8f23bb3ffb..7acba0d8be 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts @@ -21,7 +21,7 @@ export class UmbBlockTypeCardElement extends UmbLitElement { (x) => x.unique, ); - @property({ type: String, attribute: false }) + @property({ type: String }) href?: string; @property({ type: String, attribute: false }) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts index 13904a94b3..c338636571 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts @@ -234,11 +234,11 @@ export abstract class UmbBlockEntryContext< } #updateCreatePaths() { - const index = this.#index.value; - if (this._entries && index !== undefined) { + if (this._entries) { this.observe( - observeMultiple([this._entries.catalogueRouteBuilder, this._entries.canCreate]), - ([catalogueRouteBuilder, canCreate]) => { + observeMultiple([this.index, this._entries.catalogueRouteBuilder, this._entries.canCreate]), + ([index, catalogueRouteBuilder, canCreate]) => { + if (index === undefined) return; if (catalogueRouteBuilder && canCreate) { this.#createBeforePath.setValue(this._entries!.getPathForCreateBlock(index)); this.#createAfterPath.setValue(this._entries!.getPathForCreateBlock(index + 1)); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts index d6be254013..df113d9b81 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts @@ -148,6 +148,13 @@ export abstract class UmbBlockManagerContext< getContentTypeNameOf(contentTypeKey: string) { return this.#contentTypes.getValue().find((x) => x.unique === contentTypeKey)?.name; } + getContentTypeKeyOf(contentTypeKey: string) { + return this.#contentTypes.getValue().find((x) => x.unique === contentTypeKey)?.unique; + } + getContentTypeHasProperties(contentTypeKey: string) { + const properties = this.#contentTypes.getValue().find((x) => x.unique === contentTypeKey)?.properties; + return properties ? properties.length > 0 : false; + } blockTypeOf(contentTypeKey: string) { return this.#blockTypes.asObservablePart((source) => source.find((x) => x.contentElementTypeKey === contentTypeKey), diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/modals/block-catalogue/block-catalogue-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/modals/block-catalogue/block-catalogue-modal.element.ts index 85c3a67e63..92de7b54d6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/modals/block-catalogue/block-catalogue-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/modals/block-catalogue/block-catalogue-modal.element.ts @@ -1,6 +1,10 @@ import { UMB_BLOCK_WORKSPACE_MODAL } from '../../workspace/index.js'; import type { UmbBlockTypeGroup, UmbBlockTypeWithGroupKey } from '@umbraco-cms/backoffice/block-type'; -import type { UmbBlockCatalogueModalData, UmbBlockCatalogueModalValue } from '@umbraco-cms/backoffice/block'; +import { + UMB_BLOCK_MANAGER_CONTEXT, + type UmbBlockCatalogueModalData, + type UmbBlockCatalogueModalValue, +} from '@umbraco-cms/backoffice/block'; import { css, html, customElement, state, repeat, nothing } from '@umbraco-cms/backoffice/external/lit'; import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; import { UMB_MODAL_CONTEXT, UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; @@ -14,8 +18,7 @@ export class UmbBlockCatalogueModalElement extends UmbModalBaseElement< UmbBlockCatalogueModalData, UmbBlockCatalogueModalValue > { - // - private _search = ''; + #search = ''; private _groupedBlocks: Array<{ name?: string; blocks: Array }> = []; @@ -28,6 +31,9 @@ export class UmbBlockCatalogueModalElement extends UmbModalBaseElement< @state() private _filtered: Array<{ name?: string; blocks: Array }> = []; + @state() + _manager?: typeof UMB_BLOCK_MANAGER_CONTEXT.TYPE; + constructor() { super(); @@ -49,6 +55,10 @@ export class UmbBlockCatalogueModalElement extends UmbModalBaseElement< }); } }); + + this.consumeContext(UMB_BLOCK_MANAGER_CONTEXT, (manager) => { + this._manager = manager; + }); } override connectedCallback() { @@ -71,10 +81,10 @@ export class UmbBlockCatalogueModalElement extends UmbModalBaseElement< } #updateFiltered() { - if (this._search.length === 0) { + if (this.#search.length === 0) { this._filtered = this._groupedBlocks; } else { - const search = this._search.toLowerCase(); + const search = this.#search.toLowerCase(); this._filtered = this._groupedBlocks.map((group) => { return { ...group, blocks: group.blocks.filter((block) => block.label?.toLocaleLowerCase().includes(search)) }; }); @@ -82,7 +92,7 @@ export class UmbBlockCatalogueModalElement extends UmbModalBaseElement< } #onSearch(e: UUIInputEvent) { - this._search = e.target.value as string; + this.#search = e.target.value as string; this.#updateFiltered(); } @@ -98,7 +108,7 @@ export class UmbBlockCatalogueModalElement extends UmbModalBaseElement< override render() { return html` - ${this.#renderViews()} ${this._openClipboard ? this.#renderClipboard() : this.#renderCreateEmpty()} + ${this.#renderViews()}${this.#renderMain()}
this.#chooseBlock(block.contentElementTypeKey)} - ?href=${this._workspacePath + .href=${this._workspacePath && this._manager!.getContentTypeHasProperties(block.contentElementTypeKey) ? `${this._workspacePath}create/${block.contentElementTypeKey}` : undefined}> diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace-editor.element.ts index ef13fc07e2..027688f399 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace-editor.element.ts @@ -1,6 +1,8 @@ +import { UMB_BLOCK_WORKSPACE_CONTEXT } from './index.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { customElement, css, html, property } from '@umbraco-cms/backoffice/external/lit'; +import { customElement, css, html, property, state, nothing } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { observeMultiple } from '@umbraco-cms/backoffice/observable-api'; @customElement('umb-block-workspace-editor') export class UmbBlockWorkspaceEditorElement extends UmbLitElement { @@ -8,10 +10,29 @@ export class UmbBlockWorkspaceEditorElement extends UmbLitElement { @property({ type: String, attribute: false }) workspaceAlias?: string; + constructor() { + super(); + this.consumeContext(UMB_BLOCK_WORKSPACE_CONTEXT, (context) => { + this.observe( + observeMultiple([ + context.isNew, + context.content.structure.ownerContentTypePart((contentType) => contentType?.name), + ]), + ([isNew, name]) => { + this._headline = this.localize.term(isNew ? 'general_add' : 'general_edit') + ' ' + name; + }, + 'observeOwnerContentElementTypeName', + ); + }); + } + + @state() + _headline: string = ''; + override render() { return this.workspaceAlias - ? html` ` - : ''; + ? html` ` + : nothing; } static override styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts index c94569ca8a..9ff8c1d3ad 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts @@ -9,11 +9,12 @@ import { import { UmbClassState, UmbObjectState, UmbStringState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { ManifestWorkspace } from '@umbraco-cms/backoffice/extension-registry'; -import { UMB_MODAL_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { UMB_MODAL_CONTEXT, type UmbModalContext } from '@umbraco-cms/backoffice/modal'; import { decodeFilePath } from '@umbraco-cms/backoffice/utils'; import { UMB_BLOCK_ENTRIES_CONTEXT, UMB_BLOCK_MANAGER_CONTEXT, + type UmbBlockWorkspaceOriginData, type UmbBlockWorkspaceData, } from '@umbraco-cms/backoffice/block'; import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property'; @@ -32,7 +33,7 @@ export class UmbBlockWorkspaceContext; #retrieveModalContext; #entityType: string; @@ -68,7 +69,7 @@ export class UmbBlockWorkspaceContext { - this.#modalContext = context; + this.#modalContext = context as any; context.onSubmit().catch(this.#modalRejected); }).asPromise(); @@ -180,7 +181,7 @@ export class UmbBlockWorkspaceContext +
${this.disabled || this.readonly ? nothing : html``}
@@ -183,7 +183,7 @@ export class UmbMultipleColorPickerItemInputElement extends UUIFormControlMixin( `, )}
- + `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-text-string-input/input-multiple-text-string-item.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-text-string-input/input-multiple-text-string-item.element.ts index 9c3979f30d..9087d92a28 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-text-string-input/input-multiple-text-string-item.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-text-string-input/input-multiple-text-string-item.element.ts @@ -79,7 +79,7 @@ export class UmbInputMultipleTextStringItemElement extends UUIFormControlMixin(U return html` ${this.disabled || this.readonly ? nothing : html``} - + - + ${when( !this.readonly, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-slot/extension-slot.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-slot/extension-slot.element.ts index a97efe397b..5f610e076c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-slot/extension-slot.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-slot/extension-slot.element.ts @@ -15,16 +15,20 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; * @augments {UmbLitElement} */ +// TODO: Refactor extension-slot and extension-with-api slot. // TODO: Fire change event. // TODO: Make property that reveals the amount of displayed/permitted extensions. @customElement('umb-extension-slot') export class UmbExtensionSlotElement extends UmbLitElement { #attached = false; - #extensionsController?: UmbExtensionsElementInitializer; + #extensionsController?: UmbExtensionsElementInitializer | UmbExtensionElementInitializer; @state() private _permitted?: Array; + @property({ type: Boolean }) + single?: boolean; + /** * The type or types of extensions to render. * @type {string | string[]} @@ -77,7 +81,6 @@ export class UmbExtensionSlotElement extends UmbLitElement { return this.#props; } set props(newVal: Record | undefined) { - // TODO, compare changes since last time. only reset the ones that changed. This might be better done by the controller is self: this.#props = newVal; if (this.#extensionsController) { this.#extensionsController.properties = newVal; @@ -88,7 +91,7 @@ export class UmbExtensionSlotElement extends UmbLitElement { @property({ type: String, attribute: 'default-element' }) public defaultElement?: string; - @property() + @property({ attribute: false }) public renderMethod?: ( extension: UmbExtensionElementInitializer, index: number, @@ -128,15 +131,17 @@ export class UmbExtensionSlotElement extends UmbLitElement { override render() { return this._permitted ? this._permitted.length > 0 - ? repeat( - this._permitted, - (ext) => ext.alias, - (ext, i) => (this.renderMethod ? this.renderMethod(ext, i) : ext.component), - ) + ? this.single + ? this.#renderExtension(this._permitted[0], 0) + : repeat(this._permitted, (ext) => ext.alias, this.#renderExtension) : html`` : ''; } + #renderExtension = (ext: UmbExtensionElementInitializer, i: number) => { + return this.renderMethod ? this.renderMethod(ext, i) : ext.component; + }; + static override styles = css` :host { display: contents; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-slot/extension-slot.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-slot/extension-slot.test.ts index c61f08f6b3..2c9defdf6d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-slot/extension-slot.test.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-slot/extension-slot.test.ts @@ -7,6 +7,8 @@ import type { UmbExtensionElementInitializer } from '@umbraco-cms/backoffice/ext @customElement('umb-test-extension-slot-manifest-element') class UmbTestExtensionSlotManifestElement extends HTMLElement {} +@customElement('umb-test-extension-slot-manifest-element-2') +class UmbTestExtensionSlotManifestElement2 extends HTMLElement {} function sleep(timeMs: number) { return new Promise((resolve) => { @@ -44,9 +46,21 @@ describe('UmbExtensionSlotElement', () => { expect(element).to.have.property('filter'); }); + it('has a props property', () => { + expect(element).to.have.property('props'); + }); + it('has a defaultElement property', () => { expect(element).to.have.property('defaultElement'); }); + + it('has a renderMethod property', () => { + expect(element).to.have.property('renderMethod'); + }); + + it('has a single property', () => { + expect(element).to.have.property('single'); + }); }); }); @@ -57,6 +71,17 @@ describe('UmbExtensionSlotElement', () => { alias: 'unit-test-ext-slot-element-manifest', name: 'unit-test-extension', elementName: 'umb-test-extension-slot-manifest-element', + weight: 200, // first is the heaviest and is therefor rendered first. + meta: { + pathname: 'test/test', + }, + }); + umbExtensionsRegistry.register({ + type: 'dashboard', + alias: 'unit-test-ext-slot-element-manifest-2', + name: 'unit-test-extension-2', + elementName: 'umb-test-extension-slot-manifest-element-2', + weight: 100, meta: { pathname: 'test/test', }, @@ -65,6 +90,7 @@ describe('UmbExtensionSlotElement', () => { afterEach(async () => { umbExtensionsRegistry.unregister('unit-test-ext-slot-element-manifest'); + umbExtensionsRegistry.unregister('unit-test-ext-slot-element-manifest-2'); }); it('renders a manifest element', async () => { @@ -73,18 +99,30 @@ describe('UmbExtensionSlotElement', () => { await sleep(20); expect(element.shadowRoot!.firstElementChild).to.be.instanceOf(UmbTestExtensionSlotManifestElement); + expect(element.shadowRoot!.childElementCount).to.be.equal(2); }); it('works with the filtering method', async () => { element = await fixture( html` x.alias === 'unit-test-ext-slot-element-manifest'}>`, + .filter=${(x: ManifestDashboard) => + x.alias === 'unit-test-ext-slot-element-manifest-2'}>`, ); await sleep(20); + expect(element.shadowRoot!.firstElementChild).to.be.instanceOf(UmbTestExtensionSlotManifestElement2); + expect(element.shadowRoot!.childElementCount).to.be.equal(1); + }); + + it('works with the single mode', async () => { + element = await fixture(html``); + + await sleep(20); + expect(element.shadowRoot!.firstElementChild).to.be.instanceOf(UmbTestExtensionSlotManifestElement); + expect(element.shadowRoot!.childElementCount).to.be.equal(1); }); it('use the render method', async () => { @@ -102,6 +140,7 @@ describe('UmbExtensionSlotElement', () => { expect(element.shadowRoot!.firstElementChild?.firstElementChild).to.be.instanceOf( UmbTestExtensionSlotManifestElement, ); + expect(element.shadowRoot!.childElementCount).to.be.equal(1); }); it('parses the props', async () => { @@ -117,6 +156,7 @@ describe('UmbExtensionSlotElement', () => { expect((element.shadowRoot!.firstElementChild as any).testProp).to.be.equal('fooBar'); expect(element.shadowRoot!.firstElementChild).to.be.instanceOf(UmbTestExtensionSlotManifestElement); + expect(element.shadowRoot!.childElementCount).to.be.equal(1); }); }); }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-with-api-slot/extension-with-api-slot.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-with-api-slot/extension-with-api-slot.element.ts index 1248a8b0a0..b9d73fac90 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-with-api-slot/extension-with-api-slot.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/components/extension-with-api-slot/extension-with-api-slot.element.ts @@ -17,6 +17,7 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; * @augments {UmbLitElement} */ +// TODO: Refactor extension-slot and extension-with-api slot. // TODO: Fire change event. // TODO: Make property that reveals the amount of displayed/permitted extensions. @customElement('umb-extension-with-api-slot') @@ -27,6 +28,9 @@ export class UmbExtensionWithApiSlotElement extends UmbLitElement { @state() private _permitted?: Array; + @property({ type: Boolean }) + single?: boolean; + /** * The type or types of extensions to render. * @type {string | string[]} @@ -177,15 +181,17 @@ export class UmbExtensionWithApiSlotElement extends UmbLitElement { override render() { return this._permitted ? this._permitted.length > 0 - ? repeat( - this._permitted, - (ext) => ext.alias, - (ext, i) => (this.renderMethod ? this.renderMethod(ext, i) : ext.component), - ) + ? this.single + ? this.#renderExtension(this._permitted[0], 0) + : repeat(this._permitted, (ext) => ext.alias, this.#renderExtension) : html`` : ''; } + #renderExtension = (ext: UmbExtensionElementAndApiInitializer, i: number) => { + return this.renderMethod ? this.renderMethod(ext, i) : ext.component; + }; + static override styles = css` :host { display: contents; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/views/settings/property-workspace-view-settings.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/views/settings/property-workspace-view-settings.element.ts index e908214675..21f0d5d93b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/views/settings/property-workspace-view-settings.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/views/settings/property-workspace-view-settings.element.ts @@ -195,7 +195,7 @@ export class UmbPropertyTypeWorkspaceViewSettingsElement extends UmbLitElement i return html`
- + - - + + - +
- + - +
Validation diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property-layout/property-layout.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property-layout/property-layout.element.ts index 1724d318a1..e7e1b41dc5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/property-layout/property-layout.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property-layout/property-layout.element.ts @@ -81,9 +81,9 @@ export class UmbPropertyLayoutElement extends UmbLitElement {
- + - +
`; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts index f617dc0c4d..646e15b699 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property/property.element.ts @@ -332,7 +332,7 @@ export class UmbPropertyElement extends UmbLitElement { if ('checkValidity' in this._element) { const dataPath = this.dataPath; this.#controlValidator = new UmbFormControlValidator(this, this._element as any, dataPath); - // We trust blindly that the dataPath is available at this stage. [NL] + // We trust blindly that the dataPath will be present at this stage and not arrive later than this moment. [NL] if (dataPath) { this.#validationMessageBinder = new UmbBindServerValidationToFormControl( this, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.test.ts index 3595ec1376..5e2e200699 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.test.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.test.ts @@ -16,9 +16,7 @@ class UmbSorterTestElement extends UmbLitElement { itemSelector: '.item', containerSelector: '#container', disabledItemSelector: '.disabled', - onChange: ({ model }) => { - this.model = model; - }, + // TODO: In theory missing model change callback? [NL] }); getAllItems() { @@ -151,6 +149,7 @@ describe('UmbSorterController', () => { describe('enable', () => { it('sets all allowed items to draggable', () => { + // [NL] I have experienced an issue with this test, it may need a little delay before testing for this. As the test relies on DOM. const items = element.getSortableItems(); expect(items.length).to.equal(3); items.forEach((item) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-picker-modal/tree-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-picker-modal/tree-picker-modal.element.ts index 5299caa3c0..9ca76c005e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-picker-modal/tree-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-picker-modal/tree-picker-modal.element.ts @@ -20,6 +20,9 @@ export class UmbTreePickerModalElement { + this._hasSelection = hasSelection; + }); this.#observePickerSelection(); this.#observeSearch(); } @@ -188,7 +194,8 @@ export class UmbTreePickerModalElement + @click=${this._submitModal} + ?disabled=${!this._hasSelection}>
`; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/selection-manager/selection.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/selection-manager/selection.manager.ts index cf0fd0aa06..d1127cbb8d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/selection-manager/selection.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/selection-manager/selection.manager.ts @@ -13,6 +13,7 @@ export class UmbSelectionManager>[], (x) => x); public readonly selection = this.#selection.asObservable(); + public readonly hasSelection = this.#selection.asObservablePart((x) => x.length > 0); #multiple = new UmbBooleanState(false); public readonly multiple = this.#multiple.asObservable(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md b/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md index 90bdcc7220..d034fb6ca4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/README.md @@ -126,4 +126,28 @@ This fact enables a property to observe if there is any Message Paths that start Validators represent a component of the Validation to be considered, but it does not represent other messages of its path. To display messages from a given data-path, a Binder is needed. We bring a few to make this happen: -UmbBindServerValidationToFormControl +### UmbBindServerValidationToFormControl + +This binder takes a Form Control Element and a data-path. +The Data Path is a JSON Path defining where the data of this input is located in the model sent to the server. + +``` + this.#validationMessageBinder = new UmbBindServerValidationToFormControl( + this, + this.querySelector('#myInput"), + "$.values.[?(@.alias = 'my-input-alias')].value", + ); +``` + +Once the binder is initialized you need to keep it updated with the value your form control represents. Notice we do not recommend using events from the form control to notify about the changes. +Instead observe the value in of your data model. + +This example is just a dummy example of how that could look: +``` + this.observe( + this.#value, + (value) => { + this.#validationMessageBinder.value = value; + }, + ); +``` diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/components/form-validation-message.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/components/form-validation-message.element.ts new file mode 100644 index 0000000000..38a4fb1181 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/components/form-validation-message.element.ts @@ -0,0 +1,97 @@ +import { UmbValidationInvalidEvent, UmbValidationValidEvent } from '../events/index.js'; +import type { UmbFormControlMixinInterface } from '../mixins/index.js'; +import { css, customElement, html, property, repeat, unsafeHTML } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; + +/** + * @description - Component for displaying one or more validation messages from UMB/UUI Form Control within the given scope. + * Notice: Only supports components that is build on the UMB / UUI FormControlMixing. + * @slot - for button contents + * @slot message - for extras in the messages container + * @see FormControlMixin + */ +@customElement('umb-form-validation-message') +export class UmbFormValidationMessageElement extends UmbLitElement { + /** + * Set the element containing Form Controls of interest. + * @type {string} + * @default + */ + @property({ reflect: false, attribute: true }) + public get for(): HTMLElement | string | null { + return this._for; + } + public set for(value: HTMLElement | string | null) { + let element = null; + if (typeof value === 'string') { + const scope = this.getRootNode(); + element = (scope as DocumentFragment)?.getElementById(value); + } else if (value instanceof HTMLElement) { + element = value; + } + const newScope = element ?? this; + const oldScope = this._for; + + if (oldScope === newScope) { + return; + } + if (oldScope !== null) { + oldScope.removeEventListener(UmbValidationInvalidEvent.TYPE, this.#onControlInvalid as EventListener); + oldScope.removeEventListener(UmbValidationValidEvent.TYPE, this.#onControlValid as EventListener); + } + this._for = newScope; + this._for.addEventListener(UmbValidationInvalidEvent.TYPE, this.#onControlInvalid as EventListener); + this._for.addEventListener(UmbValidationValidEvent.TYPE, this.#onControlValid as EventListener); + } + private _for: HTMLElement | null = null; + + constructor() { + super(); + if (this.for === null) { + this.for = this; + } + } + + private _messages = new Map, string>(); + + #onControlInvalid = async (e: UmbValidationInvalidEvent) => { + const ctrl = (e as any).composedPath()[0]; + if (ctrl.pristine === false) { + // Currently we only show message from components who does have the pristine property. (we only want to show messages from fields that are NOT pristine aka. that are dirty or in a from that has been submitted) + // Notice we use the localization controller here, this is different frm the UUI component which uses the same name. + this._messages.set(ctrl, this.localize.string(ctrl.validationMessage)); + } else { + this._messages.delete(ctrl); + } + this.requestUpdate(); + }; + + #onControlValid = (e: UmbValidationValidEvent) => { + const ctrl = (e as any).composedPath()[0]; + this._messages.delete(ctrl); + this.requestUpdate(); + }; + + override render() { + return html` + +
+ ${repeat(this._messages, (item) => html`
${unsafeHTML(item[1])}
`)} + +
+ `; + } + + static override styles = [ + css` + #messages { + color: var(--uui-color-danger-standalone); + } + `, + ]; +} +declare global { + interface HTMLElementTagNameMap { + 'umb-form-validation-message': UmbFormValidationMessageElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts index ecb51f74ac..4231347296 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts @@ -1,9 +1,10 @@ +export * from './components/form-validation-message.element.js'; export * from './const.js'; export * from './context/index.js'; export * from './controllers/index.js'; +export * from './directives/bind-to-validation.lit-directive.js'; export * from './events/index.js'; export * from './interfaces/index.js'; export * from './mixins/index.js'; export * from './translators/index.js'; export * from './utils/index.js'; -export * from './directives/bind-to-validation.lit-directive.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts index 4a634d197c..e4f45a5518 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts @@ -80,6 +80,7 @@ export declare abstract class UmbFormControlMixinElement ) => UmbFormControlValidatorConfig; removeValidator: (obj: UmbFormControlValidatorConfig) => void; protected addFormControlElement(element: UmbNativeFormControlElement): void; + protected removeFormControlElement(element: UmbNativeFormControlElement): void; //static formAssociated: boolean; protected getFormElement(): HTMLElement | undefined | null; @@ -96,10 +97,11 @@ export declare abstract class UmbFormControlMixinElement } /** + * @mixin * The mixin allows a custom element to participate in HTML forms. * @param {object} superClass - superclass to be extended. - * @param defaultValue - * @mixin + * @param {object} defaultValue - Default value for the form control. + * @returns {Function} - The mixin class. */ export function UmbFormControlMixin< ValueType = FormData | FormDataEntryValue, @@ -172,7 +174,7 @@ export function UmbFormControlMixin< * 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. * @function getFormElement - * @returns {HTMLElement | undefined | null} + * @returns {HTMLElement | undefined | null} - Returns the form element or undefined if not found. */ protected getFormElement(): HTMLElement | undefined | null { return this.#formCtrlElements.find((el) => el.validity.valid === false); @@ -181,7 +183,7 @@ export function UmbFormControlMixin< /** * Focus first element that is invalid. * @function focusFirstInvalidElement - * @returns {HTMLElement | undefined} + * @returns {HTMLElement | undefined} - Returns the first invalid element or undefined if no invalid elements are found. */ focusFirstInvalidElement() { const firstInvalid = this.#formCtrlElements.find((el) => el.validity.valid === false); @@ -219,6 +221,7 @@ export function UmbFormControlMixin< * @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. + * @returns {UmbFormControlValidatorConfig} - The added validator configuration. */ addValidator( flagKey: FlagTypes, @@ -249,19 +252,18 @@ export function UmbFormControlMixin< } } + #runValidatorsCallback = () => this._runValidators; + /** * @function 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 {UmbNativeFormControlElement} - element to validate and include as part of this form association. + * @param {UmbNativeFormControlElement} element - element to validate and include as part of this form control association. + * @returns {void} */ protected addFormControlElement(element: UmbNativeFormControlElement) { this.#formCtrlElements.push(element); - element.addEventListener(UmbValidationInvalidEvent.TYPE, () => { - this._runValidators(); - }); - element.addEventListener(UmbValidationValidEvent.TYPE, () => { - this._runValidators(); - }); + element.addEventListener(UmbValidationInvalidEvent.TYPE, this.#runValidatorsCallback); + element.addEventListener(UmbValidationValidEvent.TYPE, this.#runValidatorsCallback); // If we are in validationMode/'touched'/not-pristine then we need to validate this newly added control. [NL] if (this._pristine === false) { element.checkValidity(); @@ -270,12 +272,29 @@ export function UmbFormControlMixin< } } + /** + * @function removeFormControlElement + * @param {UmbNativeFormControlElement} element - element to remove as part of this form controls associated controls. + * @returns {void} + */ + protected removeFormControlElement(element: UmbNativeFormControlElement) { + const index = this.#formCtrlElements.indexOf(element); + if (index !== -1) { + this.#formCtrlElements.splice(index, 1); + element.removeEventListener(UmbValidationInvalidEvent.TYPE, this.#runValidatorsCallback); + element.removeEventListener(UmbValidationValidEvent.TYPE, this.#runValidatorsCallback); + if (this._pristine === false) { + this._runValidators(); + } + } + } + private _customValidityObject?: UmbFormControlValidatorConfig; /** * @function setCustomValidity * @description Set custom validity state, set to empty string to remove the custom message. - * @param message {string} - The message to be shown + * @param {string} message - The message to be shown * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/setCustomValidity|HTMLObjectElement:setCustomValidity} */ protected setCustomValidity(message: string | null) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/views/info/data-type-workspace-view-info-reference.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/views/info/data-type-workspace-view-info-reference.element.ts index 29f19e05fd..5e34b90128 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/views/info/data-type-workspace-view-info-reference.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/views/info/data-type-workspace-view-info-reference.element.ts @@ -105,6 +105,9 @@ export class UmbDataTypeWorkspaceViewInfoReferenceElement extends UmbLitElement static override styles = [ UmbTextStyles, css` + :host { + display: contents; + } uui-table-cell { color: var(--uui-color-text-alt); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info-history.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info-history.element.ts index 2808d9bc26..a7745267f2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info-history.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info-history.element.ts @@ -97,16 +97,14 @@ export class UmbDocumentWorkspaceViewInfoHistoryElement extends UmbLitElement { override render() { return html` -
-

History

- - ${this.localize.term('actions_rollback')} - -
+ History + + ${this.localize.term('actions_rollback')} + ${this._items ? this.#renderHistory() : html` `} ${this.#renderPagination()}
`; @@ -167,18 +165,6 @@ export class UmbDocumentWorkspaceViewInfoHistoryElement extends UmbLitElement { font-size: 2rem; } - #rollback { - display: flex; - width: 100%; - align-items: center; - justify-content: space-between; - } - - #rollback h2 { - font-size: var(--uui-type-h5-size); - margin: 0; - } - uui-tag uui-icon { margin-right: var(--uui-size-space-1); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info-reference.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info-reference.element.ts index 8b4de39cb6..b1324e5ac9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info-reference.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info-reference.element.ts @@ -175,6 +175,9 @@ export class UmbDocumentWorkspaceViewInfoReferenceElement extends UmbLitElement static override styles = [ UmbTextStyles, css` + :host { + display: contents; + } uui-table-cell:not(.link-cell) { color: var(--uui-color-text-alt); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/info/media-workspace-view-info-reference.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/info/media-workspace-view-info-reference.element.ts index 6795b57e2d..ebf9da81fe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/info/media-workspace-view-info-reference.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/info/media-workspace-view-info-reference.element.ts @@ -196,6 +196,10 @@ export class UmbMediaWorkspaceViewInfoReferenceElement extends UmbLitElement { static override styles = [ UmbTextStyles, css` + :host { + display: contents; + } + uui-table-cell { color: var(--uui-color-text-alt); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/modals/change-password/change-password-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/change-password/change-password-modal.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/user/modals/change-password/change-password-modal.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/user/change-password/change-password-modal.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/change-password/change-user-password.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/change-password/change-user-password.action.ts similarity index 59% rename from src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/change-password/change-user-password.action.ts rename to src/Umbraco.Web.UI.Client/src/packages/user/change-password/change-user-password.action.ts index a052d53082..fbf0e35929 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/change-password/change-user-password.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/change-password/change-user-password.action.ts @@ -1,8 +1,9 @@ -import { UmbChangeUserPasswordRepository } from '../../repository/index.js'; +import { UmbChangeUserPasswordRepository } from '@umbraco-cms/backoffice/user'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import { UMB_MODAL_MANAGER_CONTEXT, UMB_CHANGE_PASSWORD_MODAL } from '@umbraco-cms/backoffice/modal'; +import { UMB_CURRENT_USER_CONTEXT, UmbCurrentUserRepository } from '@umbraco-cms/backoffice/current-user'; export class UmbChangeUserPasswordEntityAction extends UmbEntityActionBase { constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { @@ -23,8 +24,16 @@ export class UmbChangeUserPasswordEntityAction extends UmbEntityActionBase = [ + { + type: 'entityAction', + kind: 'default', + alias: 'Umb.EntityAction.User.ChangePassword', + name: 'Change User Password Entity Action', + weight: 600, + api: () => import('./change-user-password.action.js'), + forEntityTypes: [UMB_USER_ENTITY_TYPE], + meta: { + icon: 'icon-key', + label: '#user_changePassword', + }, + }, + { + type: 'modal', + alias: 'Umb.Modal.ChangePassword', + name: 'Change Password Modal', + js: () => import('./change-password-modal.element.js'), + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/index.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/index.ts index 5a4c7ecb02..343c0ed765 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/index.ts @@ -2,6 +2,8 @@ export * from './action/index.js'; export * from './components/index.js'; export * from './history/current-user-history.store.js'; export * from './utils/index.js'; +export * from './repository/index.js'; export * from './current-user.context.js'; export * from './current-user.context.token.js'; + export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/change-password-current-user.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/change-password-current-user.action.ts index e7607510f4..b37cfaeaa7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/change-password-current-user.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/change-password-current-user.action.ts @@ -1,9 +1,9 @@ import { UMB_CURRENT_USER_CONTEXT } from '../current-user.context.token.js'; +import { UmbCurrentUserRepository } from '../repository/index.js'; import { UmbActionBase } from '@umbraco-cms/backoffice/action'; import type { UmbCurrentUserAction, UmbCurrentUserActionArgs } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UMB_CHANGE_PASSWORD_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; - export class UmbChangePasswordCurrentUserAction extends UmbActionBase> implements UmbCurrentUserAction @@ -32,13 +32,17 @@ export class UmbChangePasswordCurrentUserAction if (!this.#unique) return; const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - modalManager.open(this, UMB_CHANGE_PASSWORD_MODAL, { + const modalContext = modalManager.open(this, UMB_CHANGE_PASSWORD_MODAL, { data: { user: { unique: this.#unique, }, }, }); + + const data = await modalContext.onSubmit(); + const repository = new UmbCurrentUserRepository(this); + await repository.changePassword(data.newPassword, data.oldPassword); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.repository.ts index a97f2257c9..e823c37939 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.repository.ts @@ -2,6 +2,8 @@ import { UmbCurrentUserServerDataSource } from './current-user.server.data-sourc import { UMB_CURRENT_USER_STORE_CONTEXT } from './current-user.store.token.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; +import type { UmbNotificationContext } from '@umbraco-cms/backoffice/notification'; +import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; /** * A repository for the current user @@ -12,6 +14,7 @@ export class UmbCurrentUserRepository extends UmbRepositoryBase { #currentUserSource = new UmbCurrentUserServerDataSource(this._host); #currentUserStore?: typeof UMB_CURRENT_USER_STORE_CONTEXT.TYPE; #init: Promise; + protected notificationContext?: UmbNotificationContext; constructor(host: UmbControllerHost) { super(host); @@ -20,6 +23,10 @@ export class UmbCurrentUserRepository extends UmbRepositoryBase { this.consumeContext(UMB_CURRENT_USER_STORE_CONTEXT, (instance) => { this.#currentUserStore = instance; }).asPromise(), + + this.consumeContext(UMB_NOTIFICATION_CONTEXT, (instance) => { + this.notificationContext = instance; + }).asPromise(), ]); } @@ -108,6 +115,27 @@ export class UmbCurrentUserRepository extends UmbRepositoryBase { return {}; } + /** + * Change password for current user + * @param userId + * @param newPassword + * @param oldPassword + * @param isCurrentUser + * @returns + */ + async changePassword(newPassword: string, oldPassword: string) { + if (!newPassword) throw new Error('New password is missing'); + if (!oldPassword) throw new Error('Old password is missing'); + + const { data, error } = await this.#currentUserSource.changePassword(newPassword, oldPassword); + + if (!error) { + const notification = { data: { message: `Password changed` } }; + this.notificationContext?.peek('positive', notification); + } + + return { data, error }; + } } export default UmbCurrentUserRepository; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts index 5e46118f53..31cc1a5694 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts @@ -115,4 +115,24 @@ export class UmbCurrentUserServerDataSource { return {}; } + + /** + * Change the password for current user + * @param id + * @param newPassword + * @param oldPassword + * @param isCurrentUser + * @returns + */ + async changePassword(newPassword: string, oldPassword: string) { + return tryExecuteAndNotify( + this.#host, + UserService.postCurrentUserByIdChangePassword({ + requestBody: { + newPassword, + oldPassword + }, + }), + ); + } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/index.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/index.ts index d3e468a0b0..28899632b9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/index.ts @@ -1,4 +1,4 @@ export { UMB_CURRENT_USER_REPOSITORY_ALIAS } from './constants.js'; export { UMB_CURRENT_USER_STORE_CONTEXT } from './current-user.store.token.js'; export { UmbCurrentUserRepository } from './current-user.repository.js'; -export { UmbCurrentUserStore } from './current-user.store.js'; +export { UmbCurrentUserStore } from './current-user.store.js'; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/manifests.ts index 16faa5c71d..96353d28f7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/manifests.ts @@ -3,7 +3,7 @@ import { manifests as userManifests } from './user/manifests.js'; import { manifests as userSectionManifests } from './user-section/manifests.js'; import { manifests as currentUserManifests } from './current-user/manifests.js'; import { manifests as userPermissionManifests } from './user-permission/manifests.js'; -import { manifests as modalManifests } from './modals/manifests.js'; +import { manifests as changePasswordManifests } from './change-password/manifests.js'; // We need to load any components that are not loaded by the user management bundle to register them in the browser. import './user-group/components/index.js'; @@ -16,5 +16,5 @@ export const manifests = [ ...userSectionManifests, ...currentUserManifests, ...userPermissionManifests, - ...modalManifests, + ...changePasswordManifests, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/modals/manifests.ts deleted file mode 100644 index 8c373a336e..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/user/modals/manifests.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { ManifestModal, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; - -const modals: Array = [ - { - type: 'modal', - alias: 'Umb.Modal.ChangePassword', - name: 'Change Password Modal', - js: () => import('./change-password/change-password-modal.element.js'), - }, -]; - -export const manifests: Array = [...modals]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/manifests.ts index 8e26c3c2f1..b67955449e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/manifests.ts @@ -55,19 +55,6 @@ const entityActions: Array = [ }, ], }, - { - type: 'entityAction', - kind: 'default', - alias: 'Umb.EntityAction.User.ChangePassword', - name: 'Change User Password Entity Action', - weight: 600, - api: () => import('./change-password/change-user-password.action.js'), - forEntityTypes: [UMB_USER_ENTITY_TYPE], - meta: { - icon: 'icon-key', - label: '#user_changePassword', - }, - }, { type: 'entityAction', kind: 'default', diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/change-password/change-user-password.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/change-password/change-user-password.server.data-source.ts index ebe00ebe02..09104ee397 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/change-password/change-user-password.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/change-password/change-user-password.server.data-source.ts @@ -33,7 +33,7 @@ export class UmbChangeUserPasswordServerDataSource { UserService.postUserByIdChangePassword({ id, requestBody: { - newPassword, + newPassword }, }), );