diff --git a/.github/workflows/azure-backoffice.yml b/.github/workflows/azure-backoffice.yml index 4c40540065..61c1d5e4c1 100644 --- a/.github/workflows/azure-backoffice.yml +++ b/.github/workflows/azure-backoffice.yml @@ -17,6 +17,7 @@ on: - main - v*/dev - v*/main + workflow_dispatch: jobs: build_and_deploy_job: diff --git a/.github/workflows/azure-storybook.yml b/.github/workflows/azure-storybook.yml index 586cd10d22..f0aa3c8a98 100644 --- a/.github/workflows/azure-storybook.yml +++ b/.github/workflows/azure-storybook.yml @@ -17,6 +17,7 @@ on: - main - v*/dev - v*/main + workflow_dispatch: env: NODE_OPTIONS: --max_old_space_size=16384 diff --git a/NOTICES.txt b/NOTICES.txt index 1ea114b715..a3cb471492 100644 --- a/NOTICES.txt +++ b/NOTICES.txt @@ -3,7 +3,9 @@ Third-Party Notices This file contains notices and attributions for third-party software used in the Umbraco CMS project. -It is not a license and does not grant any rights to use the third-party software. +Third-party software may contain dependencies that are not explicitly listed here. + +This notice is not a license and does not grant any rights to use the third-party software. Umbraco CMS is licensed under the MIT License, which can be found in the LICENSE file. @@ -113,14 +115,6 @@ Copyright: 2023 Shannon Deminick --- -Glob: A library for matching file paths using glob patterns - -URL: https://github.com/isaacs/node-glob -License: ISC License -Copyright: 2009-2023 Isaac Z. Schlueter and Contributors - ---- - Globals: A library for managing global variables in JavaScript URL: https://github.com/sindresorhus/globals @@ -356,38 +350,6 @@ Copyright: Titus Wormer --- -Rollup: A module bundler for JavaScript - -URL: https://rollupjs.org/ -License: MIT License -Copyright: 2015-present Rollup contributors - ---- - -Rollup Plugins: A collection of Rollup plugins - -URL: https://github.com/rollup/plugins -License: MIT License -Copyright: 2019-present Rollup Plugins contributors - ---- - -Rollup-plugin-esbuild: A Rollup plugin for using esbuild - -URL: https://github.com/egoist/rollup-plugin-esbuild -License: MIT License -Copyright: 2020 EGOIST - ---- - -Rollup-plugin-import-css: A Rollup plugin for importing CSS files - -URL: https://github.com/jleeson/rollup-plugin-import-css -License: MIT License -Copyright: 2020 Jacob Leeson - ---- - rxjs: Reactive Extensions for JavaScript URL: https://rxjs.dev/ diff --git a/src/Umbraco.Cms.Api.Management/Controllers/User/UserOrCurrentUserControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/User/UserOrCurrentUserControllerBase.cs index 43db8355b7..a6faf7ddbe 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/User/UserOrCurrentUserControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/User/UserOrCurrentUserControllerBase.cs @@ -25,7 +25,7 @@ public abstract class UserOrCurrentUserControllerBase : ManagementApiControllerB .Build()), UserOperationStatus.NoUserGroup => BadRequest(problemDetailsBuilder .WithTitle("No User Group Specified") - .WithDetail("A user group must be specified to create a user") + .WithDetail("A user must be assigned to at least one group") .Build()), UserOperationStatus.UserNameIsNotEmail => BadRequest(problemDetailsBuilder .WithTitle("Invalid Username") diff --git a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDatabaseCreator.cs b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDatabaseCreator.cs index c666547c1d..348846d3b1 100644 --- a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDatabaseCreator.cs +++ b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteDatabaseCreator.cs @@ -86,6 +86,7 @@ public class SqliteDatabaseCreator : IDatabaseCreator // Copy our blank(ish) wal mode sqlite database to its final location. try { + EnsureDatabaseDirectory(original.DataSource); File.Copy(tempFile, original.DataSource, true); } catch (Exception ex) @@ -104,4 +105,13 @@ public class SqliteDatabaseCreator : IDatabaseCreator _logger.LogWarning(ex, "Unable to cleanup temporary sqlite database file {path}", tempFile); } } + + private static void EnsureDatabaseDirectory(string dataSource) + { + var directoryPath = Path.GetDirectoryName(dataSource); + if (string.IsNullOrEmpty(directoryPath) is false && Directory.Exists(directoryPath) is false) + { + Directory.CreateDirectory(directoryPath); + } + } } diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index d7bb403444..ecc5989ef6 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -883,7 +883,7 @@ internal partial class UserService : RepositoryService, IUserService return UserOperationStatus.DuplicateUserName; } - if(model.UserGroupKeys.Count == 0) + if (model.UserGroupKeys.Count == 0) { return UserOperationStatus.NoUserGroup; } @@ -912,6 +912,13 @@ internal partial class UserService : RepositoryService, IUserService return Attempt.FailWithStatus(UserOperationStatus.MissingUser, existingUser); } + // A user must remain assigned to at least one group. + if (model.UserGroupKeys.Count == 0) + { + scope.Complete(); + return Attempt.FailWithStatus(UserOperationStatus.NoUserGroup, existingUser); + } + // User names can only contain the configured allowed characters. This is validated by ASP.NET Identity on create // as the setting is applied to the BackOfficeIdentityOptions, but we need to check ourselves for updates. var allowedUserNameCharacters = _securitySettings.AllowedUserNameCharacters; diff --git a/src/Umbraco.PublishedCache.HybridCache/Factories/IPublishedContentFactory.cs b/src/Umbraco.PublishedCache.HybridCache/Factories/IPublishedContentFactory.cs index c5bfe4fe9e..46cb65d5e4 100644 --- a/src/Umbraco.PublishedCache.HybridCache/Factories/IPublishedContentFactory.cs +++ b/src/Umbraco.PublishedCache.HybridCache/Factories/IPublishedContentFactory.cs @@ -1,12 +1,25 @@ -using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; namespace Umbraco.Cms.Infrastructure.HybridCache.Factories; +/// +/// Defines a factory to create and from a or . +/// internal interface IPublishedContentFactory { + /// + /// Converts a to an if document type. + /// IPublishedContent? ToIPublishedContent(ContentCacheNode contentCacheNode, bool preview); + + /// + /// Converts a to an of media type. + /// IPublishedContent? ToIPublishedMedia(ContentCacheNode contentCacheNode); + /// + /// Converts a to an . + /// IPublishedMember ToPublishedMember(IMember member); } diff --git a/src/Umbraco.PublishedCache.HybridCache/Factories/PublishedContentFactory.cs b/src/Umbraco.PublishedCache.HybridCache/Factories/PublishedContentFactory.cs index 90c26397e7..625f5594c4 100644 --- a/src/Umbraco.PublishedCache.HybridCache/Factories/PublishedContentFactory.cs +++ b/src/Umbraco.PublishedCache.HybridCache/Factories/PublishedContentFactory.cs @@ -1,30 +1,62 @@ +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Extensions; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.HybridCache.Factories; +/// +/// Defines a factory to create and from a or . +/// internal sealed class PublishedContentFactory : IPublishedContentFactory { private readonly IElementsCache _elementsCache; private readonly IVariationContextAccessor _variationContextAccessor; private readonly IPublishedContentTypeCache _publishedContentTypeCache; + private readonly ILogger _logger; + private readonly AppCaches _appCaches; - + /// + /// Initializes a new instance of the class. + /// public PublishedContentFactory( IElementsCache elementsCache, IVariationContextAccessor variationContextAccessor, - IPublishedContentTypeCache publishedContentTypeCache) + IPublishedContentTypeCache publishedContentTypeCache, + ILogger logger, + AppCaches appCaches) { _elementsCache = elementsCache; _variationContextAccessor = variationContextAccessor; _publishedContentTypeCache = publishedContentTypeCache; + _logger = logger; + _appCaches = appCaches; } + /// public IPublishedContent? ToIPublishedContent(ContentCacheNode contentCacheNode, bool preview) { - IPublishedContentType contentType = _publishedContentTypeCache.Get(PublishedItemType.Content, contentCacheNode.ContentTypeId); + var cacheKey = $"{nameof(PublishedContentFactory)}DocumentCache_{contentCacheNode.Id}_{preview}"; + IPublishedContent? publishedContent = _appCaches.RequestCache.GetCacheItem(cacheKey); + if (publishedContent is not null) + { + _logger.LogDebug( + "Using cached IPublishedContent for document {ContentCacheNodeName} ({ContentCacheNodeId}).", + contentCacheNode.Data?.Name ?? "No Name", + contentCacheNode.Id); + return publishedContent; + } + + _logger.LogDebug( + "Creating IPublishedContent for document {ContentCacheNodeName} ({ContentCacheNodeId}).", + contentCacheNode.Data?.Name ?? "No Name", + contentCacheNode.Id); + + IPublishedContentType contentType = + _publishedContentTypeCache.Get(PublishedItemType.Content, contentCacheNode.ContentTypeId); var contentNode = new ContentNode( contentCacheNode.Id, contentCacheNode.Key, @@ -35,19 +67,42 @@ internal sealed class PublishedContentFactory : IPublishedContentFactory preview ? contentCacheNode.Data : null, preview ? null : contentCacheNode.Data); - IPublishedContent? model = GetModel(contentNode, preview); + publishedContent = GetModel(contentNode, preview); if (preview) { - return model ?? GetPublishedContentAsDraft(model); + publishedContent ??= GetPublishedContentAsDraft(publishedContent); } - return model; + if (publishedContent is not null) + { + _appCaches.RequestCache.Set(cacheKey, publishedContent); + } + + return publishedContent; } + /// public IPublishedContent? ToIPublishedMedia(ContentCacheNode contentCacheNode) { - IPublishedContentType contentType = _publishedContentTypeCache.Get(PublishedItemType.Media, contentCacheNode.ContentTypeId); + var cacheKey = $"{nameof(PublishedContentFactory)}MediaCache_{contentCacheNode.Id}"; + IPublishedContent? publishedContent = _appCaches.RequestCache.GetCacheItem(cacheKey); + if (publishedContent is not null) + { + _logger.LogDebug( + "Using cached IPublishedContent for media {ContentCacheNodeName} ({ContentCacheNodeId}).", + contentCacheNode.Data?.Name ?? "No Name", + contentCacheNode.Id); + return publishedContent; + } + + _logger.LogDebug( + "Creating IPublishedContent for media {ContentCacheNodeName} ({ContentCacheNodeId}).", + contentCacheNode.Data?.Name ?? "No Name", + contentCacheNode.Id); + + IPublishedContentType contentType = + _publishedContentTypeCache.Get(PublishedItemType.Media, contentCacheNode.ContentTypeId); var contentNode = new ContentNode( contentCacheNode.Id, contentCacheNode.Key, @@ -58,12 +113,38 @@ internal sealed class PublishedContentFactory : IPublishedContentFactory null, contentCacheNode.Data); - return GetModel(contentNode, false); + publishedContent = GetModel(contentNode, false); + + if (publishedContent is not null) + { + _appCaches.RequestCache.Set(cacheKey, publishedContent); + } + + return publishedContent; } + /// public IPublishedMember ToPublishedMember(IMember member) { - IPublishedContentType contentType = _publishedContentTypeCache.Get(PublishedItemType.Member, member.ContentTypeId); + string cacheKey = $"{nameof(PublishedContentFactory)}MemberCache_{member.Id}"; + IPublishedMember? publishedMember = _appCaches.RequestCache.GetCacheItem(cacheKey); + if (publishedMember is not null) + { + _logger.LogDebug( + "Using cached IPublishedMember for member {MemberName} ({MemberId}).", + member.Username, + member.Id); + + return publishedMember; + } + + _logger.LogDebug( + "Creating IPublishedMember for member {MemberName} ({MemberId}).", + member.Username, + member.Id); + + IPublishedContentType contentType = + _publishedContentTypeCache.Get(PublishedItemType.Member, member.ContentTypeId); // Members are only "mapped" never cached, so these default values are a bit weird, but they are not used. var contentData = new ContentData( @@ -86,7 +167,11 @@ internal sealed class PublishedContentFactory : IPublishedContentFactory contentType, null, contentData); - return new PublishedMember(member, contentNode, _elementsCache, _variationContextAccessor); + publishedMember = new PublishedMember(member, contentNode, _elementsCache, _variationContextAccessor); + + _appCaches.RequestCache.Set(cacheKey, publishedMember); + + return publishedMember; } private static Dictionary GetPropertyValues(IPublishedContentType contentType, IMember member) @@ -135,7 +220,6 @@ internal sealed class PublishedContentFactory : IPublishedContentFactory _variationContextAccessor); } - private static IPublishedContent? GetPublishedContentAsDraft(IPublishedContent? content) => content == null ? null : // an object in the cache is either an IPublishedContentOrMedia, @@ -150,7 +234,7 @@ internal sealed class PublishedContentFactory : IPublishedContentFactory content = wrapped.Unwrap(); } - if (!(content is PublishedContent inner)) + if (content is not PublishedContent inner) { throw new InvalidOperationException("Innermost content is not PublishedContent."); } diff --git a/src/Umbraco.Web.UI.Client/devops/icons/index.js b/src/Umbraco.Web.UI.Client/devops/icons/index.js index b3c90e057c..811e031200 100644 --- a/src/Umbraco.Web.UI.Client/devops/icons/index.js +++ b/src/Umbraco.Web.UI.Client/devops/icons/index.js @@ -1,6 +1,7 @@ +/* eslint-disable local-rules/enforce-umbraco-external-imports */ import { readFileSync, writeFile, mkdir, rmSync } from 'fs'; -import * as globModule from 'tiny-glob'; import * as pathModule from 'path'; +import * as globModule from 'tiny-glob'; import { optimize } from 'svgo'; const path = pathModule.default; @@ -23,7 +24,7 @@ const run = async () => { // Empty output directory: rmSync(iconsOutputDirectory, { recursive: true }); - var icons = await collectDictionaryIcons(); + let icons = await collectDictionaryIcons(); icons = await collectDiskIcons(icons); writeIconsToDisk(icons); generateJS(icons); @@ -59,7 +60,7 @@ const collectDictionaryIcons = async () => { }; icons.push(icon); - } catch (e) { + } catch { errors.push(`[Lucide] Could not load file: '${path}'`); console.log(`[Lucide] Could not load file: '${path}'`); } @@ -91,7 +92,7 @@ const collectDictionaryIcons = async () => { }; icons.push(icon); - } catch (e) { + } catch { errors.push(`[SimpleIcons] Could not load file: '${path}'`); console.log(`[SimpleIcons] Could not load file: '${path}'`); } @@ -117,7 +118,7 @@ const collectDictionaryIcons = async () => { }; icons.push(icon); - } catch (e) { + } catch { errors.push(`[Umbraco] Could not load file: '${path}'`); console.log(`[Umbraco] Could not load file: '${path}'`); } @@ -171,11 +172,9 @@ const writeIconsToDisk = (icons) => { writeFileWithDir(icon.output, content, (err) => { if (err) { - // eslint-disable-next-line no-undef console.log(err); } - // eslint-disable-next-line no-undef //console.log(`icon: ${icon.name} generated`); }); }); @@ -192,18 +191,18 @@ const generateJS = (icons) => { ${icon.legacy ? 'legacy: true,' : ''} ${icon.hidden || icon.legacy ? 'hidden: true,' : ''} path: () => import("./icons/${icon.fileName}.js"), - }`.replace(/\t/g, '').replace(/^\s*[\r\n]/gm, ''); // Regex removes white space [NL] // + regex that removes empty lines. [NL] + }` + .replace(/\t/g, '') // Regex removes white space [NL] + .replace(/^\s*[\r\n]/gm, ''); // Regex that removes empty lines. [NL] }); const content = `export default [${iconDescriptors.join(',')}];`; writeFileWithDir(JSPath, content, (err) => { if (err) { - // eslint-disable-next-line no-undef console.log(err); } - // eslint-disable-next-line no-undef console.log('Icons outputted and Icon Manifests generated!'); }); }; diff --git a/src/Umbraco.Web.UI.Client/devops/module-dependencies/index.js b/src/Umbraco.Web.UI.Client/devops/module-dependencies/index.js index d6e5febf2e..39746504b5 100644 --- a/src/Umbraco.Web.UI.Client/devops/module-dependencies/index.js +++ b/src/Umbraco.Web.UI.Client/devops/module-dependencies/index.js @@ -4,7 +4,7 @@ import { createImportMap } from '../importmap/index.js'; const ILLEGAL_CORE_IMPORTS_THRESHOLD = 5; const SELF_IMPORTS_THRESHOLD = 0; -const BIDIRECTIONAL_IMPORTS_THRESHOLD = 18; +const BIDIRECTIONAL_IMPORTS_THRESHOLD = 16; const clientProjectRoot = path.resolve(import.meta.dirname, '../../'); const modulePrefix = '@umbraco-cms/backoffice/'; diff --git a/src/Umbraco.Web.UI.Client/examples/section-sidebar-menu-expansion/collapse-menu-item-entity-action/collapse-menu-item.entity-action.ts b/src/Umbraco.Web.UI.Client/examples/section-sidebar-menu-expansion/collapse-menu-item-entity-action/collapse-menu-item.entity-action.ts new file mode 100644 index 0000000000..7df5c2dc25 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/examples/section-sidebar-menu-expansion/collapse-menu-item-entity-action/collapse-menu-item.entity-action.ts @@ -0,0 +1,11 @@ +import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; +import { UMB_MENU_ITEM_CONTEXT } from '@umbraco-cms/backoffice/menu'; + +export class ExampleCollapseMenuItemEntityAction extends UmbEntityActionBase { + override async execute() { + const context = await this.getContext(UMB_MENU_ITEM_CONTEXT); + context?.expansion.collapseAll(); + } +} + +export { ExampleCollapseMenuItemEntityAction as api }; diff --git a/src/Umbraco.Web.UI.Client/examples/section-sidebar-menu-expansion/collapse-menu-item-entity-action/manifests.ts b/src/Umbraco.Web.UI.Client/examples/section-sidebar-menu-expansion/collapse-menu-item-entity-action/manifests.ts new file mode 100644 index 0000000000..69123c7f0c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/examples/section-sidebar-menu-expansion/collapse-menu-item-entity-action/manifests.ts @@ -0,0 +1,15 @@ +export const manifests: Array = [ + { + type: 'entityAction', + kind: 'default', + alias: 'Umb.EntityAction.Document.MenuItem.Collapse', + name: 'Collapse Document Menu Item', + api: () => import('./collapse-menu-item.entity-action.js'), + forEntityTypes: ['document-type-root', 'media-type-root', 'member-type-root', 'data-type-root'], + weight: -10, + meta: { + label: 'Collapse Menu Item', + icon: 'icon-wand', + }, + }, +]; diff --git a/src/Umbraco.Web.UI.Client/examples/section-sidebar-menu-expansion/index.ts b/src/Umbraco.Web.UI.Client/examples/section-sidebar-menu-expansion/index.ts new file mode 100644 index 0000000000..c911275efd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/examples/section-sidebar-menu-expansion/index.ts @@ -0,0 +1,4 @@ +import { manifests as playgroundDashboardManifests } from './playground-dashboard/manifests.js'; +import { manifests as collapseMenuItemManifests } from './collapse-menu-item-entity-action/manifests.js'; + +export const manifests: Array = [...playgroundDashboardManifests, ...collapseMenuItemManifests]; diff --git a/src/Umbraco.Web.UI.Client/examples/section-sidebar-menu-expansion/playground-dashboard/manifests.ts b/src/Umbraco.Web.UI.Client/examples/section-sidebar-menu-expansion/playground-dashboard/manifests.ts new file mode 100644 index 0000000000..d9d47be8b0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/examples/section-sidebar-menu-expansion/playground-dashboard/manifests.ts @@ -0,0 +1,14 @@ +export const manifests: Array = [ + { + type: 'dashboard', + kind: 'default', + name: 'Example Section Sidebar Menu Playground Dashboard', + alias: 'Example.Dashboard.SectionSidebarMenuPlayground', + element: () => import('./section-sidebar-menu-playground.element.js'), + weight: 3000, + meta: { + label: 'Section Sidebar Menu Playground', + pathname: 'section-sidebar-menu-playground', + }, + }, +]; diff --git a/src/Umbraco.Web.UI.Client/examples/section-sidebar-menu-expansion/playground-dashboard/section-sidebar-menu-playground.element.ts b/src/Umbraco.Web.UI.Client/examples/section-sidebar-menu-expansion/playground-dashboard/section-sidebar-menu-playground.element.ts new file mode 100644 index 0000000000..5f82b4fc45 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/examples/section-sidebar-menu-expansion/playground-dashboard/section-sidebar-menu-playground.element.ts @@ -0,0 +1,102 @@ +import { html, customElement, LitElement, css, state, repeat } from '@umbraco-cms/backoffice/external/lit'; +import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api'; +import { + UMB_SECTION_SIDEBAR_MENU_GLOBAL_CONTEXT, + UMB_SECTION_SIDEBAR_MENU_SECTION_CONTEXT, + type UmbSectionMenuItemExpansionEntryModel, +} from '@umbraco-cms/backoffice/menu'; + +@customElement('example-section-sidebar-menu-playground-dashboard') +export class ExampleSectionSidebarMenuPlaygroundDashboard extends UmbElementMixin(LitElement) { + #globalContext?: typeof UMB_SECTION_SIDEBAR_MENU_GLOBAL_CONTEXT.TYPE; + #sectionContext?: typeof UMB_SECTION_SIDEBAR_MENU_SECTION_CONTEXT.TYPE; + + @state() + private _globalExpansion: Array = []; + + @state() + private _sectionExpansion: Array = []; + + constructor() { + super(); + + this.consumeContext(UMB_SECTION_SIDEBAR_MENU_GLOBAL_CONTEXT, (section) => { + this.#globalContext = section; + this.#observeGlobalExpansion(); + }); + + this.consumeContext(UMB_SECTION_SIDEBAR_MENU_SECTION_CONTEXT, (section) => { + this.#sectionContext = section; + this.#observeSectionExpansion(); + }); + } + + #observeGlobalExpansion() { + this.observe(this.#globalContext?.expansion.expansion, (items) => { + this._globalExpansion = items || []; + }); + } + + #observeSectionExpansion() { + this.observe(this.#sectionContext?.expansion.expansion, (items) => { + this._sectionExpansion = items || []; + }); + } + + #onCloseItem(event: PointerEvent, item: UmbSectionMenuItemExpansionEntryModel) { + event.stopPropagation(); + this.#sectionContext?.expansion.collapseItem(item); + } + + #onCollapseSection() { + this.#sectionContext?.expansion.collapseAll(); + } + + override render() { + return html` + + + + Collapse All + ${repeat( + this._sectionExpansion, + (item) => item.entityType + item.unique, + (item) => this.#renderItem(item), + )} + + + ${repeat( + this._globalExpansion, + (item) => item.entityType + item.unique, + (item) => this.#renderItem(item), + )} + + `; + } + + #renderItem(item: UmbSectionMenuItemExpansionEntryModel) { + return html` + this.#onCloseItem(event, item)}>Close + `; + } + + static override styles = [ + css` + :host { + display: block; + padding: var(--uui-size-layout-1); + } + `, + ]; +} + +export { ExampleSectionSidebarMenuPlaygroundDashboard as element }; + +declare global { + interface HTMLElementTagNameMap { + 'example-section-sidebar-menu-playground-dashboard': ExampleSectionSidebarMenuPlaygroundDashboard; + } +} diff --git a/src/Umbraco.Web.UI.Client/examples/workspace-view-hint/hint-workspace-view.ts b/src/Umbraco.Web.UI.Client/examples/workspace-view-hint/hint-workspace-view.ts index 24a4d2ce03..8678114981 100644 --- a/src/Umbraco.Web.UI.Client/examples/workspace-view-hint/hint-workspace-view.ts +++ b/src/Umbraco.Web.UI.Client/examples/workspace-view-hint/hint-workspace-view.ts @@ -46,7 +46,7 @@ export class ExampleHintWorkspaceView extends UmbElementMixin(LitElement) { } else { workspace.hints.addOne({ unique: 'exampleHintFromToggleAction', - path: ['Umb.WorkspaceView.Document.Edit', 'root'], + path: ['Umb.WorkspaceView.Document.Edit'], text: 'Hi', color: 'invalid', weight: 100, diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index de7c946839..c04e89a769 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -44,7 +44,6 @@ "eslint-plugin-storybook": "9.0.14", "eslint-plugin-wc": "^2.2.1", "globals": "^16.3.0", - "lucide-static": "^0.539.0", "madge": "^8.0.0", "msw": "^1.3.5", "playwright-msw": "^3.0.1", @@ -52,16 +51,14 @@ "postcss-cli": "^11.0.1", "prettier": "3.6.2", "remark-gfm": "^4.0.1", - "simple-icons": "^14.15.0", "storybook": "9.0.14", - "svgo": "^3.3.2", "tiny-glob": "^0.2.9", "tsc-alias": "^1.8.16", "typedoc": "^0.28.10", "typescript": "5.9.2", "typescript-eslint": "^8.39.1", "typescript-json-schema": "^0.65.1", - "vite": "^7.1.2", + "vite": "^7.1.3", "vite-plugin-static-copy": "^3.1.2", "vite-tsconfig-paths": "^5.1.4", "web-component-analyzer": "^2.0.0" @@ -1034,7 +1031,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.0.6.tgz", "integrity": "sha512-yktiFZoWPtEW8QKS65eqKwA5MTKp88CyiL8q72WynrBs/73SAaxlSWlA2zW/DZlywZ5hX1OYzrCC0wFdvO9c2w==", - "dev": true, "license": "MIT", "dependencies": { "@jsdevtools/ono": "^7.1.3", @@ -1050,10 +1046,9 @@ } }, "node_modules/@hey-api/openapi-ts": { - "version": "0.71.1", - "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.71.1.tgz", - "integrity": "sha512-LZhHH1CngE2vRgMdZGRI4UrxJ/JHvcsAy4Qjsbiu9BGWyZIraIqWLMsQgqB3TU5yJTrblFo9DA7RI5LIUol0yg==", - "dev": true, + "version": "0.81.1", + "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.81.1.tgz", + "integrity": "sha512-DdLEMbfQRMRDd+cOxG91G7Ro7vUUtt/zYlBGiYTahVO/4rn1HHnYl/WHL+to5I7CTX3sVVsVrxHWcJd0Fue7wg==", "license": "MIT", "dependencies": { "@hey-api/json-schema-ref-parser": "1.0.6", @@ -1061,7 +1056,10 @@ "c12": "2.0.1", "color-support": "1.1.3", "commander": "13.0.0", - "handlebars": "4.7.8" + "handlebars": "4.7.8", + "js-yaml": "4.1.0", + "open": "10.1.2", + "semver": "7.7.2" }, "bin": { "openapi-ts": "bin/index.cjs" @@ -1076,6 +1074,63 @@ "typescript": "^5.5.3" } }, + "node_modules/@hey-api/openapi-ts/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@hey-api/openapi-ts/node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@hey-api/openapi-ts/node_modules/open": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.2.tgz", + "integrity": "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@hey-api/openapi-ts/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1214,7 +1269,6 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", - "dev": true, "license": "MIT" }, "node_modules/@lit-labs/ssr-dom-shim": { @@ -2602,16 +2656,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/@ts-graphviz/adapter": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@ts-graphviz/adapter/-/adapter-2.0.6.tgz", @@ -2985,7 +3029,6 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, "license": "MIT" }, "node_modules/@types/json5": { @@ -3694,909 +3737,909 @@ "link": true }, "node_modules/@umbraco-ui/uui": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui/-/uui-1.15.0-rc.0.tgz", - "integrity": "sha512-zO5uCoiXy8f6aGP6k5fCWchKT/6c/UnDlfquODG5tQioFF3pqIfInN/0JHUgMJsQQARdm5KFkSp0PWvVmXhX9A==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui/-/uui-1.15.0.tgz", + "integrity": "sha512-8yCzfKePv9zK+PMw1K3uwpnDDBZkbSCCOrt8dNDlZ1/6zf+yNLu+r/kVlMcauei6aNsCwXvcI4LhBGS1f5kJ/A==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-action-bar": "1.15.0-rc.0", - "@umbraco-ui/uui-avatar": "1.15.0-rc.0", - "@umbraco-ui/uui-avatar-group": "1.15.0-rc.0", - "@umbraco-ui/uui-badge": "1.15.0-rc.0", - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-boolean-input": "1.15.0-rc.0", - "@umbraco-ui/uui-box": "1.15.0-rc.0", - "@umbraco-ui/uui-breadcrumbs": "1.15.0-rc.0", - "@umbraco-ui/uui-button": "1.15.0-rc.0", - "@umbraco-ui/uui-button-copy-text": "1.15.0-rc.0", - "@umbraco-ui/uui-button-group": "1.15.0-rc.0", - "@umbraco-ui/uui-button-inline-create": "1.15.0-rc.0", - "@umbraco-ui/uui-card": "1.15.0-rc.0", - "@umbraco-ui/uui-card-block-type": "1.15.0-rc.0", - "@umbraco-ui/uui-card-content-node": "1.15.0-rc.0", - "@umbraco-ui/uui-card-media": "1.15.0-rc.0", - "@umbraco-ui/uui-card-user": "1.15.0-rc.0", - "@umbraco-ui/uui-caret": "1.15.0-rc.0", - "@umbraco-ui/uui-checkbox": "1.15.0-rc.0", - "@umbraco-ui/uui-color-area": "1.15.0-rc.0", - "@umbraco-ui/uui-color-picker": "1.15.0-rc.0", - "@umbraco-ui/uui-color-slider": "1.15.0-rc.0", - "@umbraco-ui/uui-color-swatch": "1.15.0-rc.0", - "@umbraco-ui/uui-color-swatches": "1.15.0-rc.0", - "@umbraco-ui/uui-combobox": "1.15.0-rc.0", - "@umbraco-ui/uui-combobox-list": "1.15.0-rc.0", - "@umbraco-ui/uui-css": "1.15.0-rc.0", - "@umbraco-ui/uui-dialog": "1.15.0-rc.0", - "@umbraco-ui/uui-dialog-layout": "1.15.0-rc.0", - "@umbraco-ui/uui-file-dropzone": "1.15.0-rc.0", - "@umbraco-ui/uui-file-preview": "1.15.0-rc.0", - "@umbraco-ui/uui-form": "1.15.0-rc.0", - "@umbraco-ui/uui-form-layout-item": "1.15.0-rc.0", - "@umbraco-ui/uui-form-validation-message": "1.15.0-rc.0", - "@umbraco-ui/uui-icon": "1.15.0-rc.0", - "@umbraco-ui/uui-icon-registry": "1.15.0-rc.0", - "@umbraco-ui/uui-icon-registry-essential": "1.15.0-rc.0", - "@umbraco-ui/uui-input": "1.15.0-rc.0", - "@umbraco-ui/uui-input-file": "1.15.0-rc.0", - "@umbraco-ui/uui-input-lock": "1.15.0-rc.0", - "@umbraco-ui/uui-input-password": "1.15.0-rc.0", - "@umbraco-ui/uui-keyboard-shortcut": "1.15.0-rc.0", - "@umbraco-ui/uui-label": "1.15.0-rc.0", - "@umbraco-ui/uui-loader": "1.15.0-rc.0", - "@umbraco-ui/uui-loader-bar": "1.15.0-rc.0", - "@umbraco-ui/uui-loader-circle": "1.15.0-rc.0", - "@umbraco-ui/uui-menu-item": "1.15.0-rc.0", - "@umbraco-ui/uui-modal": "1.15.0-rc.0", - "@umbraco-ui/uui-pagination": "1.15.0-rc.0", - "@umbraco-ui/uui-popover": "1.15.0-rc.0", - "@umbraco-ui/uui-popover-container": "1.15.0-rc.0", - "@umbraco-ui/uui-progress-bar": "1.15.0-rc.0", - "@umbraco-ui/uui-radio": "1.15.0-rc.0", - "@umbraco-ui/uui-range-slider": "1.15.0-rc.0", - "@umbraco-ui/uui-ref": "1.15.0-rc.0", - "@umbraco-ui/uui-ref-list": "1.15.0-rc.0", - "@umbraco-ui/uui-ref-node": "1.15.0-rc.0", - "@umbraco-ui/uui-ref-node-data-type": "1.15.0-rc.0", - "@umbraco-ui/uui-ref-node-document-type": "1.15.0-rc.0", - "@umbraco-ui/uui-ref-node-form": "1.15.0-rc.0", - "@umbraco-ui/uui-ref-node-member": "1.15.0-rc.0", - "@umbraco-ui/uui-ref-node-package": "1.15.0-rc.0", - "@umbraco-ui/uui-ref-node-user": "1.15.0-rc.0", - "@umbraco-ui/uui-scroll-container": "1.15.0-rc.0", - "@umbraco-ui/uui-select": "1.15.0-rc.0", - "@umbraco-ui/uui-slider": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-expand": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-file": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-file-dropzone": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-file-thumbnail": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-folder": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-lock": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-more": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-sort": "1.15.0-rc.0", - "@umbraco-ui/uui-table": "1.15.0-rc.0", - "@umbraco-ui/uui-tabs": "1.15.0-rc.0", - "@umbraco-ui/uui-tag": "1.15.0-rc.0", - "@umbraco-ui/uui-textarea": "1.15.0-rc.0", - "@umbraco-ui/uui-toast-notification": "1.15.0-rc.0", - "@umbraco-ui/uui-toast-notification-container": "1.15.0-rc.0", - "@umbraco-ui/uui-toast-notification-layout": "1.15.0-rc.0", - "@umbraco-ui/uui-toggle": "1.15.0-rc.0", - "@umbraco-ui/uui-visually-hidden": "1.15.0-rc.0" + "@umbraco-ui/uui-action-bar": "1.15.0", + "@umbraco-ui/uui-avatar": "1.15.0", + "@umbraco-ui/uui-avatar-group": "1.15.0", + "@umbraco-ui/uui-badge": "1.15.0", + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-boolean-input": "1.15.0", + "@umbraco-ui/uui-box": "1.15.0", + "@umbraco-ui/uui-breadcrumbs": "1.15.0", + "@umbraco-ui/uui-button": "1.15.0", + "@umbraco-ui/uui-button-copy-text": "1.15.0", + "@umbraco-ui/uui-button-group": "1.15.0", + "@umbraco-ui/uui-button-inline-create": "1.15.0", + "@umbraco-ui/uui-card": "1.15.0", + "@umbraco-ui/uui-card-block-type": "1.15.0", + "@umbraco-ui/uui-card-content-node": "1.15.0", + "@umbraco-ui/uui-card-media": "1.15.0", + "@umbraco-ui/uui-card-user": "1.15.0", + "@umbraco-ui/uui-caret": "1.15.0", + "@umbraco-ui/uui-checkbox": "1.15.0", + "@umbraco-ui/uui-color-area": "1.15.0", + "@umbraco-ui/uui-color-picker": "1.15.0", + "@umbraco-ui/uui-color-slider": "1.15.0", + "@umbraco-ui/uui-color-swatch": "1.15.0", + "@umbraco-ui/uui-color-swatches": "1.15.0", + "@umbraco-ui/uui-combobox": "1.15.0", + "@umbraco-ui/uui-combobox-list": "1.15.0", + "@umbraco-ui/uui-css": "1.15.0", + "@umbraco-ui/uui-dialog": "1.15.0", + "@umbraco-ui/uui-dialog-layout": "1.15.0", + "@umbraco-ui/uui-file-dropzone": "1.15.0", + "@umbraco-ui/uui-file-preview": "1.15.0", + "@umbraco-ui/uui-form": "1.15.0", + "@umbraco-ui/uui-form-layout-item": "1.15.0", + "@umbraco-ui/uui-form-validation-message": "1.15.0", + "@umbraco-ui/uui-icon": "1.15.0", + "@umbraco-ui/uui-icon-registry": "1.15.0", + "@umbraco-ui/uui-icon-registry-essential": "1.15.0", + "@umbraco-ui/uui-input": "1.15.0", + "@umbraco-ui/uui-input-file": "1.15.0", + "@umbraco-ui/uui-input-lock": "1.15.0", + "@umbraco-ui/uui-input-password": "1.15.0", + "@umbraco-ui/uui-keyboard-shortcut": "1.15.0", + "@umbraco-ui/uui-label": "1.15.0", + "@umbraco-ui/uui-loader": "1.15.0", + "@umbraco-ui/uui-loader-bar": "1.15.0", + "@umbraco-ui/uui-loader-circle": "1.15.0", + "@umbraco-ui/uui-menu-item": "1.15.0", + "@umbraco-ui/uui-modal": "1.15.0", + "@umbraco-ui/uui-pagination": "1.15.0", + "@umbraco-ui/uui-popover": "1.15.0", + "@umbraco-ui/uui-popover-container": "1.15.0", + "@umbraco-ui/uui-progress-bar": "1.15.0", + "@umbraco-ui/uui-radio": "1.15.0", + "@umbraco-ui/uui-range-slider": "1.15.0", + "@umbraco-ui/uui-ref": "1.15.0", + "@umbraco-ui/uui-ref-list": "1.15.0", + "@umbraco-ui/uui-ref-node": "1.15.0", + "@umbraco-ui/uui-ref-node-data-type": "1.15.0", + "@umbraco-ui/uui-ref-node-document-type": "1.15.0", + "@umbraco-ui/uui-ref-node-form": "1.15.0", + "@umbraco-ui/uui-ref-node-member": "1.15.0", + "@umbraco-ui/uui-ref-node-package": "1.15.0", + "@umbraco-ui/uui-ref-node-user": "1.15.0", + "@umbraco-ui/uui-scroll-container": "1.15.0", + "@umbraco-ui/uui-select": "1.15.0", + "@umbraco-ui/uui-slider": "1.15.0", + "@umbraco-ui/uui-symbol-expand": "1.15.0", + "@umbraco-ui/uui-symbol-file": "1.15.0", + "@umbraco-ui/uui-symbol-file-dropzone": "1.15.0", + "@umbraco-ui/uui-symbol-file-thumbnail": "1.15.0", + "@umbraco-ui/uui-symbol-folder": "1.15.0", + "@umbraco-ui/uui-symbol-lock": "1.15.0", + "@umbraco-ui/uui-symbol-more": "1.15.0", + "@umbraco-ui/uui-symbol-sort": "1.15.0", + "@umbraco-ui/uui-table": "1.15.0", + "@umbraco-ui/uui-tabs": "1.15.0", + "@umbraco-ui/uui-tag": "1.15.0", + "@umbraco-ui/uui-textarea": "1.15.0", + "@umbraco-ui/uui-toast-notification": "1.15.0", + "@umbraco-ui/uui-toast-notification-container": "1.15.0", + "@umbraco-ui/uui-toast-notification-layout": "1.15.0", + "@umbraco-ui/uui-toggle": "1.15.0", + "@umbraco-ui/uui-visually-hidden": "1.15.0" } }, "node_modules/@umbraco-ui/uui-action-bar": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-action-bar/-/uui-action-bar-1.15.0-rc.0.tgz", - "integrity": "sha512-hrAIlv2MEg+BcgxLg0H1b0hsyfGjiLNRAiHXf9GUfDNb6RZs9wCEgnI88vU8pfnTIRkfVDqewm5WEBxYwucSQw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-action-bar/-/uui-action-bar-1.15.0.tgz", + "integrity": "sha512-O0807+bWVWV/rsFihFVKSOkg9wBtLXKCszE5+eZk2KmONm93BFhIAE35rp7eD6X2SuJMHwYzInIxMIMjHzdpUQ==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-button-group": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-button-group": "1.15.0" } }, "node_modules/@umbraco-ui/uui-avatar": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-avatar/-/uui-avatar-1.15.0-rc.0.tgz", - "integrity": "sha512-DbisxrMnN1Z2yV+kQduTHu3Jw6gCRQlQLt/H5GQySNek1zNqaI0M8yeU6CHZPis+DgZoP0MaFRXyRuTqOhe2iQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-avatar/-/uui-avatar-1.15.0.tgz", + "integrity": "sha512-eEX83zwRN3tCiHScKcmkROWAfLu3TgFI9SntlbyxiuSSYfhJxWSZXOf6lVvQ/1CyvKq8XqSbBnN3VKXgcaKOpg==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-avatar-group": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-avatar-group/-/uui-avatar-group-1.15.0-rc.0.tgz", - "integrity": "sha512-tetsqYKGEv69RMRpeamAVzNQMP77Ty7NIYaRBNfhsyAoYCE2uL1DxQsqiUzv2Ve2dDmRM5GOrWD4zxhrFHhoiQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-avatar-group/-/uui-avatar-group-1.15.0.tgz", + "integrity": "sha512-CGYAFAHgNoQK2UTupP7pO0mwP6t9/Ms6WZ0gIC40a+kPjrGtaDWU52hiPsuXrUcR6WjJwZ5WRrJHOboRpdmM0g==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-avatar": "1.15.0-rc.0", - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-avatar": "1.15.0", + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-badge": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-badge/-/uui-badge-1.15.0-rc.0.tgz", - "integrity": "sha512-VFxMixI9AXu2FlHlY4djnzURNpIAYjCvgfgmZG81HRbfgtSsv/nCS/l2dC5Y0/oDku/yDkF73ZqeosQAOREyQA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-badge/-/uui-badge-1.15.0.tgz", + "integrity": "sha512-9aGmhRvey98kcR7wfsnno0BNftIRwJ0r2lCH6cNK2lhe69enYm0MWjp+4uutnlEWWskTLEI344BOqmqOHH1NPA==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-base": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-base/-/uui-base-1.15.0-rc.0.tgz", - "integrity": "sha512-xj0NMdQ+dVdVvo4Y8FAFYYtVt84T/KkzVPi+jSsgt/n0pWdfFd3f13cYiUYehNcS1q+rZxXp0ECuxnoi9GTzQg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-base/-/uui-base-1.15.0.tgz", + "integrity": "sha512-0vtKmjzUOn/tIUHNrsx7aZpy3eq9aRKqV9kkJTrhH92S4WcMy+cOB1iw9t3Fe3xlBPuL3JpszwuxMTIuIqJTgQ==", "license": "MIT", "peerDependencies": { "lit": ">=2.8.0" } }, "node_modules/@umbraco-ui/uui-boolean-input": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-boolean-input/-/uui-boolean-input-1.15.0-rc.0.tgz", - "integrity": "sha512-BVQVhEdOz+YJQQ0iFFJqDpW8wvsa9423GcIa0x8QRj7KhDgiEcbwNM3eHblHYkmKXFYVd250/MIB7UurY5IycA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-boolean-input/-/uui-boolean-input-1.15.0.tgz", + "integrity": "sha512-LkYX+p44mFVdvlZSliP5ClMcyHoOIVLWI3WVkaMLQdNi2LO9bbfaTneLzu4ruW6v0iF+kLsznr3bl73VHk7oEg==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-box": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-box/-/uui-box-1.15.0-rc.0.tgz", - "integrity": "sha512-u9RxTBDzbggQu6MHNFYSRU4siMLeMO4y/O6jbxRd/8mPViiT14yobY7I1d1wOYbgevoltJrtHqhDOjIWj+x0TQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-box/-/uui-box-1.15.0.tgz", + "integrity": "sha512-MhSNcKsVNymD/yt3NFXujuaQmAqMqj5S+CBoDHEk88H7Id9NMw9RStZFJ37mI2CxHWkeHDotNVgOhSBiHJNJnw==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-css": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-css": "1.15.0" } }, "node_modules/@umbraco-ui/uui-breadcrumbs": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-breadcrumbs/-/uui-breadcrumbs-1.15.0-rc.0.tgz", - "integrity": "sha512-3hAq5GbnIbf2RfCbfW59T5uYm6nX0Zobp+VWtGb9388FhhkZuSQdn2nuN33VjmMwvVkTvGWcuqww4ii+XoSaTg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-breadcrumbs/-/uui-breadcrumbs-1.15.0.tgz", + "integrity": "sha512-TaUY+hNB0VIwv9SBi9fDjIFRtrmmkcT7hlhLCJLUVfQ7jJlGLPISAAdypSplNeCPthYvP1cJQ9m28OzscXHZxQ==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-button": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button/-/uui-button-1.15.0-rc.0.tgz", - "integrity": "sha512-5YAmsYyQHzY8HBbfZpK7ekrLQKm90Zuq/TpcOuWGCe9h33T97CWJ+ekcNzpFWBhDXKPfcV22wSapnszzsPxI4g==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button/-/uui-button-1.15.0.tgz", + "integrity": "sha512-3Oaqj6Yta/Q/Ndme20YA1XbHdBBL71iNhpqREfTHli2YV4TEcgIiNy0s2op2oPhKjIEQPEfitU2BrruYEEWa7Q==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-icon-registry-essential": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-icon-registry-essential": "1.15.0" } }, "node_modules/@umbraco-ui/uui-button-copy-text": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button-copy-text/-/uui-button-copy-text-1.15.0-rc.0.tgz", - "integrity": "sha512-HeV6yEZ6do7JkJfDLTZqD1Z4EHuev65KFx3JVNHSg+fDfFQgs1fcM4Hrm7wBUsOLcIknvislCs8UumypdGeIBQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button-copy-text/-/uui-button-copy-text-1.15.0.tgz", + "integrity": "sha512-MAaJzpwVnlyGJNvLv6qIwrYsI5SaXXiVKgVi47I8+x//QmnArmetCN04766gGzmAb0m2uuC3ht0BXMDv05pxvw==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-button": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-button": "1.15.0" } }, "node_modules/@umbraco-ui/uui-button-group": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button-group/-/uui-button-group-1.15.0-rc.0.tgz", - "integrity": "sha512-C8goOUkPe6LzVJbkeIIMzEBpqt8rkqGwpLMxYnEnkFx5HW+pQK8JHEivpGiznzl3mGLglPZC/WxnTtKuNb9ZYg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button-group/-/uui-button-group-1.15.0.tgz", + "integrity": "sha512-YPEnubKNbKmw04eWRH24/3Uxu+zhtLPeJoaT6ykPCyjr/EKc82rSTvn8fwQuP41UokQrXOac2pKn7SncyoST1Q==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-button-inline-create": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button-inline-create/-/uui-button-inline-create-1.15.0-rc.0.tgz", - "integrity": "sha512-4TOly3u5cWBJFBJZvtNkYNM0pnuiEz2u0kRQhrXOhutXH7jJ19yxSrAg+KntZDGalyd22a5nv7L3+P6kTiLibA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button-inline-create/-/uui-button-inline-create-1.15.0.tgz", + "integrity": "sha512-nLJZ6P5eK1rYgqjP5zCxbZp8g4WJ23RnUZQ49o7QpU/7zoPOK72/fuM3Ky00Iapixm/kAD6dYHO/P+GtNz8/CA==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-card": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card/-/uui-card-1.15.0-rc.0.tgz", - "integrity": "sha512-A/sKTH8BxtjeEFFp1ou8v9TNBCw2jkMdXly3Ps+TmQZW/I0/bfZoRMOhHqLU+QyfF6/JH4v+IruVtcjqZJizMg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card/-/uui-card-1.15.0.tgz", + "integrity": "sha512-pNjpk2iIdSsmTtDdBsWaEr8JX0RcWbl8yKGaqLvo/S7d3bly5z+FjcsgGnX1i1GHo7dqmgVJfbdvN9V1jgn+FA==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-checkbox": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-checkbox": "1.15.0" } }, "node_modules/@umbraco-ui/uui-card-block-type": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-block-type/-/uui-card-block-type-1.15.0-rc.0.tgz", - "integrity": "sha512-KZtZof1Tuierk9ztnoYcSqbtwFevBCYYnv4poTaoRTwoigxN1rK+aEdVZFejmHg9Jn6BxV6RIHjDI1P0+388tA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-block-type/-/uui-card-block-type-1.15.0.tgz", + "integrity": "sha512-cWag+D0XrogYZesAN8NMPQCCuU7L7uZ4Xz8dmirKQk1gjMrFDC4vYPZRQ/5O3ETTFupfDipVKimgRDsmarbLSQ==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-card": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-card": "1.15.0" } }, "node_modules/@umbraco-ui/uui-card-content-node": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-content-node/-/uui-card-content-node-1.15.0-rc.0.tgz", - "integrity": "sha512-Bzw4gH+3f/gCVJZIV9+V7tzQrqIVUUy7Y44ZsMunwcSWUKldrKLfJvwkG2RmhrUmxlus8MYEJRHOEX2fQqJ4zQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-content-node/-/uui-card-content-node-1.15.0.tgz", + "integrity": "sha512-DZ6JYNvGb5wVkhhLShENMm+Y6kTpz37YrApQTJVUUgPXhIABO2CDCnqgpH5tkQX73s9jjVB3Ca7SeivuYv8G9A==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-card": "1.15.0-rc.0", - "@umbraco-ui/uui-icon": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-card": "1.15.0", + "@umbraco-ui/uui-icon": "1.15.0" } }, "node_modules/@umbraco-ui/uui-card-media": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-media/-/uui-card-media-1.15.0-rc.0.tgz", - "integrity": "sha512-SDrY63OyNvV3HjhAy+bNONX/cGTX+9Oe8ItlJvX/r7a8qN6j1QaoechUXCVDC4rlAwpaiyPILQ+hPs2eizevzw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-media/-/uui-card-media-1.15.0.tgz", + "integrity": "sha512-EzYebWCzR0wHY902NmAcTRSVSscYac3QntCz+xwSulrhzfy4copeOd1qE+Lz7FjHs+ho0IBPZol8sF4W6rK8FQ==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-card": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-file": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-folder": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-card": "1.15.0", + "@umbraco-ui/uui-symbol-file": "1.15.0", + "@umbraco-ui/uui-symbol-folder": "1.15.0" } }, "node_modules/@umbraco-ui/uui-card-user": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-user/-/uui-card-user-1.15.0-rc.0.tgz", - "integrity": "sha512-19a9edL1zmwK5ufyV15hYU6k5aWcLTf6YCOqjcZsnVZfZTpqu22pnDHH1eA5KYh4usGFIG6fTS5dQh1b/o7y6w==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-user/-/uui-card-user-1.15.0.tgz", + "integrity": "sha512-oo7gCs3RGJ4ujFs+LpG9I1DS/XSNkz9gaqvp4BkaR0kBXzw5f2SLLGhA9S3M6M+OPlsXuuJNKlTV1tn2+LF6Ng==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-avatar": "1.15.0-rc.0", - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-card": "1.15.0-rc.0" + "@umbraco-ui/uui-avatar": "1.15.0", + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-card": "1.15.0" } }, "node_modules/@umbraco-ui/uui-caret": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-caret/-/uui-caret-1.15.0-rc.0.tgz", - "integrity": "sha512-ym+6OpksGsJDL7TkRtnvnGu+1kq6VOyxKlvYR+1pqATQfU61t8DBPTuZXYdyiXLKkh5iOSxmxnoq7jIYi89VSA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-caret/-/uui-caret-1.15.0.tgz", + "integrity": "sha512-cnKP5GeaI028hGabVCki1kPqAVSekFeP7QEwu7lncA+dcX8uvg+ffV6kW9FV0emOhI5Tmxvh8o+UDKlLs28q3A==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-checkbox": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-checkbox/-/uui-checkbox-1.15.0-rc.0.tgz", - "integrity": "sha512-vCXJi1x3HGr25l1BubJcJJU6XN+Mw7Wlq1sCThS5MBBDi/SaZojV0Xpo5EwREnf8kNn2ozKG5qhKCfHLptO0QQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-checkbox/-/uui-checkbox-1.15.0.tgz", + "integrity": "sha512-vPkgrFAPDMvJdJTADIWNj48D8gJWD3dBotucUghg/wHhvJv8h/2MvnwMUNnnSB1REHbanl7hJBVvKcNkoil0gA==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-boolean-input": "1.15.0-rc.0", - "@umbraco-ui/uui-icon-registry-essential": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-boolean-input": "1.15.0", + "@umbraco-ui/uui-icon-registry-essential": "1.15.0" } }, "node_modules/@umbraco-ui/uui-color-area": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-area/-/uui-color-area-1.15.0-rc.0.tgz", - "integrity": "sha512-MB0iM4pCVEs3JCBHMUa4Zu9AQJt+89JkZuTHzOMJKaZG31rWx4Jm5BzLazstnxRTkrBsY3IRq/PSnU7bT5jXUA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-area/-/uui-color-area-1.15.0.tgz", + "integrity": "sha512-k6u//b+s6UYmzKYMizIf2MRGD4kFy1qWdSk1GnIeDdiQxABJuBZEtkACIe66j+lxnonFvZ/assbLbhRiu15ZCw==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", + "@umbraco-ui/uui-base": "1.15.0", "colord": "^2.9.3" } }, "node_modules/@umbraco-ui/uui-color-picker": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-picker/-/uui-color-picker-1.15.0-rc.0.tgz", - "integrity": "sha512-Qxd0jRZtCkpoKkWaNLs9zGqqynXLA57XDDz3BpC/KdyUDL9Ab8HDUdtZJdTNla7+Paf2kZ0jqINClas/0AiWaA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-picker/-/uui-color-picker-1.15.0.tgz", + "integrity": "sha512-I4KGyzZZazjeifcavHp7qnMbP0Jh0dM+gzZhV+YtdPR2JT0o7y6stkbY0f+dOln0K6Bu6BQLV0HLHl/1f/1NDg==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-popover-container": "1.15.0-rc.0", + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-popover-container": "1.15.0", "colord": "^2.9.3" } }, "node_modules/@umbraco-ui/uui-color-slider": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-slider/-/uui-color-slider-1.15.0-rc.0.tgz", - "integrity": "sha512-gRKjEEB93OBLxkkq8rnix8Ve5hIIHkjgsO0EoolEHUNx0R3E9nlzc0fkG1xdht3Ifd20CFQS7PsQ3ciJjUvQwg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-slider/-/uui-color-slider-1.15.0.tgz", + "integrity": "sha512-lpT9kapypGkTelG9COSk169VKs0MSiKweX8menDDn0p6I4RfKQBy0N27HecCcf1RqPsCnTbP3lPr5DJy00KdzA==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-color-swatch": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-swatch/-/uui-color-swatch-1.15.0-rc.0.tgz", - "integrity": "sha512-LuyExHAC9nv2gK1sd4QolUreAAvqfNzQR1iDggfRqx9RG+X7yXaxssDRZ0L8uL7ga1sefYSQlYO0L28e2hpLCw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-swatch/-/uui-color-swatch-1.15.0.tgz", + "integrity": "sha512-1AI0QMr046fKc8xZ4aBO7FDwvggsS9plIpY0W4AGrqQxqGUR2u/mTU49+8xMtboaFOen5RQpJ65DN9hAgeNZ+w==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-icon-registry-essential": "1.15.0-rc.0", + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-icon-registry-essential": "1.15.0", "colord": "^2.9.3" } }, "node_modules/@umbraco-ui/uui-color-swatches": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-swatches/-/uui-color-swatches-1.15.0-rc.0.tgz", - "integrity": "sha512-OPT9M21/5dHxEMXdxMH6Vum5XDPhSqCgJ81OKvXk/KfYCMXdh8mfkOu+EqchKL2bgDsAbc5eaqk5aLqTwVF+hA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-swatches/-/uui-color-swatches-1.15.0.tgz", + "integrity": "sha512-UzlgWdsVHyCM/znFThrfA4A/S/K/R9Nc2KyRYiyy2xgBoP7x2vJ5Rn4mnR02W4bhI3gNgCJ2fqhmyiW4dxyk0Q==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-color-swatch": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-color-swatch": "1.15.0" } }, "node_modules/@umbraco-ui/uui-combobox": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-combobox/-/uui-combobox-1.15.0-rc.0.tgz", - "integrity": "sha512-vkJZT4bno93Xyamky2vM1dVh8vClX2nm7P3Vyd/drBCihcBDcijv7tRCn+97HzEXyUO5aEs4lV43niyOzsRIoQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-combobox/-/uui-combobox-1.15.0.tgz", + "integrity": "sha512-CKslvVRCKCReMr/ZZh4wc3TKJNvFjKVm/hSIvFqCIoJuSKfC4XuLU9SK9FL1s42NUAUmccSD3hATZJZ9VXqY+Q==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-button": "1.15.0-rc.0", - "@umbraco-ui/uui-combobox-list": "1.15.0-rc.0", - "@umbraco-ui/uui-icon": "1.15.0-rc.0", - "@umbraco-ui/uui-popover-container": "1.15.0-rc.0", - "@umbraco-ui/uui-scroll-container": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-expand": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-button": "1.15.0", + "@umbraco-ui/uui-combobox-list": "1.15.0", + "@umbraco-ui/uui-icon": "1.15.0", + "@umbraco-ui/uui-popover-container": "1.15.0", + "@umbraco-ui/uui-scroll-container": "1.15.0", + "@umbraco-ui/uui-symbol-expand": "1.15.0" } }, "node_modules/@umbraco-ui/uui-combobox-list": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-combobox-list/-/uui-combobox-list-1.15.0-rc.0.tgz", - "integrity": "sha512-7kCNzHY51hBY3YTXzCTWfsmyMMbyxPb6ClXyeUBA2DAihP/YNm73lT3zSpR9eXowppEbnTFM8NpMa3PSqu67cw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-combobox-list/-/uui-combobox-list-1.15.0.tgz", + "integrity": "sha512-e8IhqU9AC5pOqXuzPzI+BDsW97Ac0u1GU/5MIJqRcBZ+ZttPcH4fsm4u8siCHbK1khCG0Vzo7HiKPZ0IuuOslg==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-css": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-css/-/uui-css-1.15.0-rc.0.tgz", - "integrity": "sha512-S+fIODQMdBjfA/WNg950AvRnKfXOP5j2IZgXRb2zk5KhDLx7Hr/Zw8KICM/7CeOTnbedCm/BvZoVrZ8bCtMCRw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-css/-/uui-css-1.15.0.tgz", + "integrity": "sha512-0FPJ/NHE8MSJ7vvdFp4cSWT5L73KS00bUHPyA6oiF/XnE0AfS9TzkSWObdSrU6kyPr/fybVwsycDZNBfPiyrCA==", "license": "MIT", "peerDependencies": { "lit": ">=2.8.0" } }, "node_modules/@umbraco-ui/uui-dialog": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-dialog/-/uui-dialog-1.15.0-rc.0.tgz", - "integrity": "sha512-j81llRkeb+cL9DIPpfI0TDxi63xdiZchNpLg59a7wI9UBgoTAdsjhml6vuNgf7x2ivmy7OF8XswP98MQSoQnxw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-dialog/-/uui-dialog-1.15.0.tgz", + "integrity": "sha512-iVsrVVnvBrCCT9uJhyBE7b1kXwWUUKDmimhs/TyF1SFjxWP/U0Z99QqqI1pawdad+BuK3oVCmxYOdaReWDQXkQ==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-css": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-css": "1.15.0" } }, "node_modules/@umbraco-ui/uui-dialog-layout": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-dialog-layout/-/uui-dialog-layout-1.15.0-rc.0.tgz", - "integrity": "sha512-0UxrGcCThIBk15lsahGGnDzJXR0QD40JfqZGkc7jMddCF/uljBn/R2Ccke1VEic6x9SiB2csPLpA9L/sIui1hA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-dialog-layout/-/uui-dialog-layout-1.15.0.tgz", + "integrity": "sha512-JdDRIzSGGDnvVqXSIhc+5rDXMdYMO+Hd7s2hqLp+iRSn8IHISN/qT1nfFVO9LMbLdcApanl3JJ4Rru9LN4Q3HA==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-file-dropzone": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-file-dropzone/-/uui-file-dropzone-1.15.0-rc.0.tgz", - "integrity": "sha512-37flID+ISdYZA9i1p7sVqjf6qwvrHtYFUYIh7xSuQCQPT+ftrg/db3S0lUaWJnslOGe6ChhLp4qSXo8p3ZLDVQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-file-dropzone/-/uui-file-dropzone-1.15.0.tgz", + "integrity": "sha512-MhJRkVdDQWKEBvemNRD4bZCuIS0JUll1nNoPK7scA+e6vDmbv25vqPHNXGE/sIpVkChY/L+v+twokzlHn57XMw==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-file-dropzone": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-symbol-file-dropzone": "1.15.0" } }, "node_modules/@umbraco-ui/uui-file-preview": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-file-preview/-/uui-file-preview-1.15.0-rc.0.tgz", - "integrity": "sha512-sFBAUDSGV2fiLV4xdcou7t1GZKCtYTETUZW/zJf0QTAoexjlYyp9hcRw7f3IhOBbARlQvFeuoIiA5e1ZZA+3aw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-file-preview/-/uui-file-preview-1.15.0.tgz", + "integrity": "sha512-AHKIdYLC0ga4Wgr68xtW/gG3NDqn+QhD2aus0l2n4lBoq6OAQ5aZiPwD9i1fCD7dgyjKQ6Ov9PJSaqRYQkOlNA==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-file": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-file-thumbnail": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-folder": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-symbol-file": "1.15.0", + "@umbraco-ui/uui-symbol-file-thumbnail": "1.15.0", + "@umbraco-ui/uui-symbol-folder": "1.15.0" } }, "node_modules/@umbraco-ui/uui-form": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form/-/uui-form-1.15.0-rc.0.tgz", - "integrity": "sha512-+drFTajJ4i+Kzv0DD8mSMb8eRHMM5l8E4ucAXVpXjQ6oo8LxRemmjFoU+wq4y/xLZeYEvRXz6m33esRHjgFhug==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form/-/uui-form-1.15.0.tgz", + "integrity": "sha512-4u9ZryfVBunpb0IL0+TevytrISA6S1+AajiK/PUk0JMJfqMuQMjmpnNPdtYRNVgFFIcQFQKipjT/mrHbDVawxw==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-form-layout-item": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form-layout-item/-/uui-form-layout-item-1.15.0-rc.0.tgz", - "integrity": "sha512-IU9WiRWC7/O+YeMRPhnJ+VCx7i+gwRIdjeCmpB8LbRBspK40PBYpHn14G4parwaKHGVfOnis+BbUjWP4TTOrMg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form-layout-item/-/uui-form-layout-item-1.15.0.tgz", + "integrity": "sha512-fiWGeQpREnl6k+6VNHz9ixNdEmOoFNm7qsgdIYJ1jCDXBGME1mjxJOr2Eq7UWJuzQM8BeyQEcXq5SVIOv21PRw==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-form-validation-message": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-form-validation-message": "1.15.0" } }, "node_modules/@umbraco-ui/uui-form-validation-message": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form-validation-message/-/uui-form-validation-message-1.15.0-rc.0.tgz", - "integrity": "sha512-B8l/jlOtAWimDC2m7/pZjnZS/rVNayYVivwsHKnQn/KN0XQ2nyYq8cpYUA1XZ+ZqDbS/0BP/UUZIC3Fx4n4Eng==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form-validation-message/-/uui-form-validation-message-1.15.0.tgz", + "integrity": "sha512-RYfwmjPkY0KumjaalLW8gkasW25Mj87YFAzJn7mAYiZigURape9RqGpvrBfwcMmGj3W2/uVuHxpAHrvweQOt4A==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-icon": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon/-/uui-icon-1.15.0-rc.0.tgz", - "integrity": "sha512-x+HjEO92m4D31Xajzet7XwmAWDNim0XMn0IDB/qxHVza3N3ciisaMiqQ1kx/196RTcKFFWVOT6d2UMwPxwS5eQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon/-/uui-icon-1.15.0.tgz", + "integrity": "sha512-e8/W6gu6kwrodH0f0U70LR5rHQhiZGq3NqLqikAQ1rvmwitXUqtkVXIhkGxSf7M6yPhpmoi2qEEZDQH9cvCE5A==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-icon-registry": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon-registry/-/uui-icon-registry-1.15.0-rc.0.tgz", - "integrity": "sha512-/iNgSrVtXUQzwYRLjAI3XcIcr0EnN00trdOFCGd/2TnEhR1QOxNHIw/0FvSqjDder1y5gYFwLregle0pOvzkow==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon-registry/-/uui-icon-registry-1.15.0.tgz", + "integrity": "sha512-nIdzCqoABeRVG6jW045ok649MiAhm5zPdfuMKc1a+TNw9xkKj+vnA1YcjaBN502+AekMhhwnqgj9mLL+mC2VPQ==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-icon": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-icon": "1.15.0" } }, "node_modules/@umbraco-ui/uui-icon-registry-essential": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon-registry-essential/-/uui-icon-registry-essential-1.15.0-rc.0.tgz", - "integrity": "sha512-sj7R4u1DYXOHpjeYnlJPA3qjdp4SZSen1/XUdbQxouANcI5a8ZCVfg1+7WIvYWIDBJ2c4VMabGkZYkAvpgLanw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon-registry-essential/-/uui-icon-registry-essential-1.15.0.tgz", + "integrity": "sha512-llHFVMlV3Uyg2fHiNt1qfDgRhLthD37uQD2FzlQb0GEYjp+4dE8Jyc/eZW2mqABPweUJACVwbrwBUVrCeQJ1OQ==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-icon-registry": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-icon-registry": "1.15.0" } }, "node_modules/@umbraco-ui/uui-input": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input/-/uui-input-1.15.0-rc.0.tgz", - "integrity": "sha512-UeoW+WzqfR01kDqgDDsRX+N7ELujghXqDoOx364kJjlwfiemh+58490bujbOT7a+AG1LDNsV27px0OJBpNsErA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input/-/uui-input-1.15.0.tgz", + "integrity": "sha512-vPc4I/kkQM9RWfHI0F/OQhoTu+KefplbQp0JEQ4gfr6MwxIm6bBTEuw8T5K9t1DQs8EZ7yeLEsSh65FDPepRtg==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-input-file": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-file/-/uui-input-file-1.15.0-rc.0.tgz", - "integrity": "sha512-NNbOzLtRPeWnzJoRqLP9BKyCrZODsuOeJuFKQUUPo6NuT2bg1SIu51PHdRcO8a709UDH/lB3wBSuwt3Be71TEw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-file/-/uui-input-file-1.15.0.tgz", + "integrity": "sha512-VVn2FMsflvEWd6fOX0HQ3JaUh7haznqSqCLTSTOduh/H3jE+dVYCW6YC5uTsxArmOwsSBYSfBQNetW23eJim3Q==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-action-bar": "1.15.0-rc.0", - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-button": "1.15.0-rc.0", - "@umbraco-ui/uui-file-dropzone": "1.15.0-rc.0", - "@umbraco-ui/uui-icon": "1.15.0-rc.0", - "@umbraco-ui/uui-icon-registry-essential": "1.15.0-rc.0" + "@umbraco-ui/uui-action-bar": "1.15.0", + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-button": "1.15.0", + "@umbraco-ui/uui-file-dropzone": "1.15.0", + "@umbraco-ui/uui-icon": "1.15.0", + "@umbraco-ui/uui-icon-registry-essential": "1.15.0" } }, "node_modules/@umbraco-ui/uui-input-lock": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-lock/-/uui-input-lock-1.15.0-rc.0.tgz", - "integrity": "sha512-CJKssXf7E8szjGRXghkyHRMo8seyXq9jxrumWyxxhJCHGOXsGecIREc1oAd/a2C+BfZzdyykGiOiJLZoJRURGw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-lock/-/uui-input-lock-1.15.0.tgz", + "integrity": "sha512-AFyVYNeExHXe10b3/5/BLZOmMKyMxzftsO0HKbaQQuxrxL2SCHsQJRUpxSY+/0vAl2JbNdmrk0HTsP1O4Y9zig==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-button": "1.15.0-rc.0", - "@umbraco-ui/uui-icon": "1.15.0-rc.0", - "@umbraco-ui/uui-input": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-button": "1.15.0", + "@umbraco-ui/uui-icon": "1.15.0", + "@umbraco-ui/uui-input": "1.15.0" } }, "node_modules/@umbraco-ui/uui-input-password": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-password/-/uui-input-password-1.15.0-rc.0.tgz", - "integrity": "sha512-H0G6xtLeWE1z8hi0cgMA4DnE6ay84Z4y2vtb5R9hvS5oocMbpybjOwaKuiV3H4qam/CUAhGtRnTqP0cEWqLahQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-password/-/uui-input-password-1.15.0.tgz", + "integrity": "sha512-Pe8lNdHz/6IfbQyWEi0o+pKJ6/zunQ2b8HARCU0a9HFXRDk+XsAuBsn79zQXZl5MvseAUQrnouLwPHpdtMbeMg==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-icon-registry-essential": "1.15.0-rc.0", - "@umbraco-ui/uui-input": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-icon-registry-essential": "1.15.0", + "@umbraco-ui/uui-input": "1.15.0" } }, "node_modules/@umbraco-ui/uui-keyboard-shortcut": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-keyboard-shortcut/-/uui-keyboard-shortcut-1.15.0-rc.0.tgz", - "integrity": "sha512-mR5SWcf+U01aJfNxtgTZxYJQ240MtxnfKNo7C/7soxLnTd1w0Pp7KRHkB6GgKlqTx0X4Umcdu8kn35cCOkoDtA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-keyboard-shortcut/-/uui-keyboard-shortcut-1.15.0.tgz", + "integrity": "sha512-8Q/G5Lg6949BbMHQ1BhZ9UpoJjOQ19w1tl2y0d/rP3w/mKnTQaBSf+MQmA/6kQ/Unb2wHXJANr4pAGpUklOg6A==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-label": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-label/-/uui-label-1.15.0-rc.0.tgz", - "integrity": "sha512-P/wuJift88Sd7fq/BASMTb9JmqpA069GttPxiIoCwMb7zt+fX+xsjjzPHiD1FLihVPUFdw9lIq6d65ahbrDnMg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-label/-/uui-label-1.15.0.tgz", + "integrity": "sha512-fnmRl+RGUROERvt+Jw0WiW3Btlddg0Xka6F+gR95gy5gr/v8s34uf1/bbPD3hWUXZPukLmxeMjbzyuqMrO8rpQ==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-loader": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader/-/uui-loader-1.15.0-rc.0.tgz", - "integrity": "sha512-XD8DsEIQhVVnXtAFNVHwH9v6ysTyKbSpHeq9pHFWmupCpqvF1i0fBRYLnE+nTyzq5331WyoQieWrsmYz2C8dTw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader/-/uui-loader-1.15.0.tgz", + "integrity": "sha512-HQ2zCp2kz45GWQ3wV153ytuYD2KcdeAA5RRUVrN0Zn3GQB3wfG7xMkQQNRAOWMUdnfqmdQHeK+COO7NaET3VBA==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-loader-bar": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader-bar/-/uui-loader-bar-1.15.0-rc.0.tgz", - "integrity": "sha512-rXH8dEagrmiWZHgdHfb7Er3FsA93ZBrvF/KF4tC4Jw+2xZC+tw+n/7yAKcWM8JpCphGaIl2e4BqoalNIazmd8g==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader-bar/-/uui-loader-bar-1.15.0.tgz", + "integrity": "sha512-4eMeerunFc5yZsJIwpHADn8oGcu0Nn36oyKbSd0qC0mNmmN2i8UOF9w4O+lndd2L0Mhv23FGvBRo7mb5EAvWlg==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-loader-circle": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader-circle/-/uui-loader-circle-1.15.0-rc.0.tgz", - "integrity": "sha512-JfjucJUjGMD9yKgOscYXPOY0KN9ABbmMTwOQTzWzWAPvY6P8/OgGUKrGAQAnZzQ2Qqcl4/MMGuN4L5HX5YC3gw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader-circle/-/uui-loader-circle-1.15.0.tgz", + "integrity": "sha512-4rG8UHvyS2qvsjQYEmYjKX01SRwfk60oH8SSSx8r3z2BM62dCOa+4SBhLxqiBciC/u8FtN8X20MIGE0+eMdtoA==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-menu-item": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-menu-item/-/uui-menu-item-1.15.0-rc.0.tgz", - "integrity": "sha512-rGe/GsgWWyPZiRw0vjW7T1i00x3IKt502ESADdfK/ViF8rE9JgVIDvwCGvYHvbDPfVQci8YsQYS8I4VX29z77w==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-menu-item/-/uui-menu-item-1.15.0.tgz", + "integrity": "sha512-BOebCMB/p4TaK4kJYrYgimC6SSGBHN4y1MytK3tyvObbuj3gVqkbwHW5CZrhK4jMaywRgGq96OsuaGfc52HFog==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-loader-bar": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-expand": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-loader-bar": "1.15.0", + "@umbraco-ui/uui-symbol-expand": "1.15.0" } }, "node_modules/@umbraco-ui/uui-modal": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-modal/-/uui-modal-1.15.0-rc.0.tgz", - "integrity": "sha512-cKO6BE5tWDIfiGAMM/8/6GmOTrEzVqpSfe4yVFX4AvChOlCfx0+NS2gffICzFq7n5BZ7lOKo5dx8ByuRDkIhrw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-modal/-/uui-modal-1.15.0.tgz", + "integrity": "sha512-EDz1Qx+mTXNvOu565IculPCyuuFHwBon2wYnfWDBMoHJ5+P54jBHzg2U/8fUVse7xKPvU21hF4JneAvycNIiGQ==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-pagination": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-pagination/-/uui-pagination-1.15.0-rc.0.tgz", - "integrity": "sha512-NaxkYnfUSQNfXVpyQyqtFefpvA4ElBaXf+BdhwxEIYvki7qLsracZxPtLahgfxm08xP+kP5smIVSNbLsOUh1Kg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-pagination/-/uui-pagination-1.15.0.tgz", + "integrity": "sha512-sPVs1bApKupNd2JcSMwFS1060Y++Fti1ybJrafcLh1+h4IjmLDIRTHTTL8C+kei5G2Oi3+Z7vGpLu7lrTAmRlw==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-button": "1.15.0-rc.0", - "@umbraco-ui/uui-button-group": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-button": "1.15.0", + "@umbraco-ui/uui-button-group": "1.15.0" } }, "node_modules/@umbraco-ui/uui-popover": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-popover/-/uui-popover-1.15.0-rc.0.tgz", - "integrity": "sha512-jkrJVREYar6tNG+DS01Wnqg656d6Cen2sUTYl/grKVsgLqdZPP42hzpClctlT8HdDYSj650e5JOCqSOFZiv2Zw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-popover/-/uui-popover-1.15.0.tgz", + "integrity": "sha512-VCHVvO0fd5eL5UvB/RPL/K68UhOgsIpuyr+aXLblaYT/6at2LNosUxR4eRW2r0WOQzOiyE+Nu69pExBKyfT8bw==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-popover-container": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-popover-container/-/uui-popover-container-1.15.0-rc.0.tgz", - "integrity": "sha512-s/j+dlKjtVhAXvs7FKwjL54xlrqfWnjqjycxIABHXasrJbLJwX8oxs2iL8pk4mb4ZbdIpdA5xkRjF4eTyfT1Jw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-popover-container/-/uui-popover-container-1.15.0.tgz", + "integrity": "sha512-54M4G0ru8j5ltPAdDGIxogdmos33hxeQeusI/uMFxo2yqHHyMHRi95vvCdcwFmGlEdFd2rsnxZKfNMUKM99GKQ==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-progress-bar": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-progress-bar/-/uui-progress-bar-1.15.0-rc.0.tgz", - "integrity": "sha512-oYGogiagAay/ZqmDgTtB1FCjXZuaN/5Lj0TiOFwdVtIO2l+xmHwyUyX7byyt2kaZ6kGcuROTlPYn1gDvK5yRUg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-progress-bar/-/uui-progress-bar-1.15.0.tgz", + "integrity": "sha512-vtGUwHaG4EDLQERkwym51OasoWLj30LQLhcCCYXDJtTL1dG2nIKScEtlSUiVh5qRsI+V+kaBYPGD4TFD5o43tQ==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-radio": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-radio/-/uui-radio-1.15.0-rc.0.tgz", - "integrity": "sha512-l/+QPKyN+9CiDhKxOfmDnbpXLO7rkLM0F8AL+fy/Jjp33NC32blQYAkYV+Ayv0H898nkZBrcpA+tvVGkoiHKeQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-radio/-/uui-radio-1.15.0.tgz", + "integrity": "sha512-5TUF/iWUzbVXvBs9Z273q6s9yLbns8itTiFHCITw5w5fZzDn8R6O5hrOW7tV79kCxAnBSOAVP8v1JhGTwXw19Q==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-range-slider": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-range-slider/-/uui-range-slider-1.15.0-rc.0.tgz", - "integrity": "sha512-P8+e+JFlEhsZHntt3XVwKQkNvluIVqnlLM3VYFlOeqU+3pgsDIat4Tm2YiH23Cd8dCI0G5eLlmqVLPvwQjhhDA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-range-slider/-/uui-range-slider-1.15.0.tgz", + "integrity": "sha512-HMHVdBoB1O39rojofezee2aXGv6CMn7dUFvNefdF9HxmNrIcpFBYXSL8aBt5QJeziFQMwbCtqyY21aUag0nzfw==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-ref": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref/-/uui-ref-1.15.0-rc.0.tgz", - "integrity": "sha512-ZQ9U8x2ypxWqFFFDu3vjXPahUbr7TmGKUSBwDai4HPdLXOQqoAR5Fc0NOYxx4fc9yq5NQ2hczK1hd71V9ST28g==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref/-/uui-ref-1.15.0.tgz", + "integrity": "sha512-w7FZIe5mtsgvsf6hOH5mHKDBzg9Rd/+viyk/xNVs1NeZBn1nWEIHZs0R7YMpv+QxulklhAOpBcbGoUTB8xE+vA==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-ref-list": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-list/-/uui-ref-list-1.15.0-rc.0.tgz", - "integrity": "sha512-th86PCdgzsaIwtjLr8AccyvHYhpoRKWVDjDEcFkfpUvbogSI8u/0fSmkaNwUjtCehoRT/HCDj9we3Yn4A10xUQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-list/-/uui-ref-list-1.15.0.tgz", + "integrity": "sha512-UT65bpUmRlEgVuvn2RlTZ5l2WDF82jH1t8g+6HV6OJctpqTKlOfPkQmd6AluESPhHFEwwTydS/M7x+X3Adkdtw==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-ref-node": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node/-/uui-ref-node-1.15.0-rc.0.tgz", - "integrity": "sha512-kw56OWmUdd2sFWeYBjcKaE4mDuLFjKv7JImwIfn8LYtZ/uTcwsdiGOQf6gQtNbMVtW5FMjJQXBFCagT+HwW0RQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node/-/uui-ref-node-1.15.0.tgz", + "integrity": "sha512-ybDqIt1cXd7AiZLZsDrSHCMp2zM8I+0lmN599b3NROjm59SZXIvpbY1TS1gJ45htgsc18x2y+S4laInYu2dGUg==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-icon": "1.15.0-rc.0", - "@umbraco-ui/uui-ref": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-icon": "1.15.0", + "@umbraco-ui/uui-ref": "1.15.0" } }, "node_modules/@umbraco-ui/uui-ref-node-data-type": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-data-type/-/uui-ref-node-data-type-1.15.0-rc.0.tgz", - "integrity": "sha512-ZYNoUVBPmuxYM3obWfuiQNJCAEv7csDMpiQJ0Eaf3W3hxm6i1HxTC6V4NajO38zFVBsJjCaXzqgESLPtN1DS+g==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-data-type/-/uui-ref-node-data-type-1.15.0.tgz", + "integrity": "sha512-59s16558ySCX7b9IT/Sorq0fdFeCRENSTa7DIkQUrvVPaFWqKFz9jCYFEqDnH11jZHGsNiYh5YCmWlF/VNbwJQ==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-ref-node": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-ref-node": "1.15.0" } }, "node_modules/@umbraco-ui/uui-ref-node-document-type": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-document-type/-/uui-ref-node-document-type-1.15.0-rc.0.tgz", - "integrity": "sha512-ZEtyl+n/IkSflZxLwhKBGTMJYndJPHBGAqijqAajDWpBYrum/bxu8bYjE+oVkRRMHqQgCm/q6B4qTrX0qsPWfQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-document-type/-/uui-ref-node-document-type-1.15.0.tgz", + "integrity": "sha512-AWPZPkFGcAkRx4j6JnOi2r3EJxnvZUXfhOWNyWB2/dFRckatPH56+lVkqV+fRC+81emKBSQWkx2NphFzLEMr0A==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-ref-node": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-ref-node": "1.15.0" } }, "node_modules/@umbraco-ui/uui-ref-node-form": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-form/-/uui-ref-node-form-1.15.0-rc.0.tgz", - "integrity": "sha512-cQER0tdwAGSzyO76RDDj/aoIDC3rLv3EobpL0BeSldYfgFQKfyS+96JthQkDytJP1RC8kJdQ8J5/QM2Z+QTDnA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-form/-/uui-ref-node-form-1.15.0.tgz", + "integrity": "sha512-knIIbbfoWtepOvyC54dCo3xF0Vuap6i5uMQPd+wITCmg56a+yiJFuke+dyzatOIeXzgspLgFUngwQZEj5mJnWA==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-ref-node": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-ref-node": "1.15.0" } }, "node_modules/@umbraco-ui/uui-ref-node-member": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-member/-/uui-ref-node-member-1.15.0-rc.0.tgz", - "integrity": "sha512-oOqA0Dl6Zc312Li8mLlTvIJ4MDiaMEGD5ip1plmSiNF7YxRIh+6srrPugAZQwsrU+q6JVEEul5HjgOd8Eu46pQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-member/-/uui-ref-node-member-1.15.0.tgz", + "integrity": "sha512-pXvL523m2JR3P8OO+E1AE4YAaYhJLc519CtjNXSuctNIk1yWvwxBu8VozLIQV+xrOXGz+SiXwDkoaRPwjTQKtg==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-ref-node": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-ref-node": "1.15.0" } }, "node_modules/@umbraco-ui/uui-ref-node-package": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-package/-/uui-ref-node-package-1.15.0-rc.0.tgz", - "integrity": "sha512-oXh030/wLZiU/9TBEfHvoGHXCRDcI43oJ0x/Den4S2U1Fc+5U4Y9F0LvA3611IL2EqUa3ZA0tQJLzAt6kPbpkw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-package/-/uui-ref-node-package-1.15.0.tgz", + "integrity": "sha512-bQWfZPKJyAf2O/YvOD6fVSSpKaYZMBsrEGT+ydLPv3BNJroYHS8+NEbulZxExWztNApTcs6Vo5T19AUz+vsnLQ==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-ref-node": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-ref-node": "1.15.0" } }, "node_modules/@umbraco-ui/uui-ref-node-user": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-user/-/uui-ref-node-user-1.15.0-rc.0.tgz", - "integrity": "sha512-KqFQAaXGdf3N+RLj3tuU0TFeedNANT2fr6dRuVysEL8hKIpjkCKdNp4PCGePY1BR97/NVLNnXXuG3+Ccued3tQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-user/-/uui-ref-node-user-1.15.0.tgz", + "integrity": "sha512-4GpRzhGedMwjqW1Wk7AvgakNCc6S1edYpHWeO6cfmryIm0hvnCfkU132lzLmB+Ag2QIOI8p4Ak0OQHYWd+XZHw==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-ref-node": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-ref-node": "1.15.0" } }, "node_modules/@umbraco-ui/uui-scroll-container": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-scroll-container/-/uui-scroll-container-1.15.0-rc.0.tgz", - "integrity": "sha512-KfBdFU2zY5IfeNUMreCwHfWw0HoEPx9+2pbmc7UCPgg7SjYMkBNa8sce+bpc/OoUUpo9zbbvNjqF6eyXqeVwGw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-scroll-container/-/uui-scroll-container-1.15.0.tgz", + "integrity": "sha512-L4qM6GPDqu0/9B2OVb3EljZT3zYxbwp6uOz1nfVYpGAWBxd6INOtNbn8WYdZLq6qqa8NR6qK+su2554nInvQGA==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-select": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-select/-/uui-select-1.15.0-rc.0.tgz", - "integrity": "sha512-IORtMOqTy/1+S6a7QRqg8LKgqRX4jLsQkz1J2PK2DtuKceX/tO5HmE+Ec9cTVSFBlDFE95uek6VEsGyGj5oGMw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-select/-/uui-select-1.15.0.tgz", + "integrity": "sha512-yRx+TlXBB05jM8ShThRooFgCS5nSN7eAAnpttZgBWqY3sccIVy2Knbkz3kXLJE6ld+bO5nUXhsZBZ34MEHkiog==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-slider": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-slider/-/uui-slider-1.15.0-rc.0.tgz", - "integrity": "sha512-CifB669wtpHvTzG9d9jKj1eVB9ePeJSO8L4DjNSCaDyvrsYxDpZYzOBQuaIpPRXwdGyznFSyOAb7d8renrlpBw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-slider/-/uui-slider-1.15.0.tgz", + "integrity": "sha512-+OAzOutyUB2WCI+e5aFRoUNsFFuc/hUXnpIjx4P1moOYiggc/NxjaTHz5mxbmkC11yyS+0vpl8lVSZglkLCH5w==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-symbol-expand": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-expand/-/uui-symbol-expand-1.15.0-rc.0.tgz", - "integrity": "sha512-GQ9SMmMVJ6b/vAMa22QVJOTRn6uFdu9IELRtwrBNDgywA5L+TG7MyJ4ZgKXc1DwAKwVXwlS0qTAWBis3z1CZRQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-expand/-/uui-symbol-expand-1.15.0.tgz", + "integrity": "sha512-6y9rpFfhtuWMnaAamlzrB5Q12dsZ8dprmQaGtKr+g97PTNRPC3/dc5sdROam8VMDAhL9MkfBAZBoS6yAoJsPcQ==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-symbol-file": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file/-/uui-symbol-file-1.15.0-rc.0.tgz", - "integrity": "sha512-8FrKoBzm1YKM2L4dpNG9DjqrQZ/qnUfnx1oLzohxWZXKn13dxS7XdDFkn9B7PPgcxkxPK5DFR2Y8H0YQCTPEIw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file/-/uui-symbol-file-1.15.0.tgz", + "integrity": "sha512-F0BueWxu6J5P7xyzkp1c/eFZJjStsw65hB3bNEmWBOqkm/jbBKg9+Xs99tov+VwCHYOt8T+DuEDkkKmdxVAxyQ==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-symbol-file-dropzone": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file-dropzone/-/uui-symbol-file-dropzone-1.15.0-rc.0.tgz", - "integrity": "sha512-89og/JhpHbcQj2NuKnbJJBKKCvUWnixMw+yEDU6K4rQ7gOEWMUiYgEI87rfpp4tIRp4pKLL8jTDrvu6I1rS6jA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file-dropzone/-/uui-symbol-file-dropzone-1.15.0.tgz", + "integrity": "sha512-D5DottbukIFxL+YTVEMujHPdqB8Hhw02TKpegfDQb8UGSPC5pCQw4O212TSuyTalKb598niNmCzcjEG5TWNqww==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-symbol-file-thumbnail": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file-thumbnail/-/uui-symbol-file-thumbnail-1.15.0-rc.0.tgz", - "integrity": "sha512-LxNSR/v8NnAs7iyhDBy+ylf8C1OxxIelm3Q7/6ko474wsaBfQqwfJUbmzqCmcQhoptDZia0VxjWB80Sh5OtkAg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file-thumbnail/-/uui-symbol-file-thumbnail-1.15.0.tgz", + "integrity": "sha512-JLcEVnJqv2LL8VtscPCtPKda9ywWzV4vd0XODHLE3iI1cgHeNwMBhxqgkah0ULuw5w2Hrq8gwQ7/DuPHMSIFxw==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-symbol-folder": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-folder/-/uui-symbol-folder-1.15.0-rc.0.tgz", - "integrity": "sha512-OqMe0IKp1qIhyQJ3Yw0CMcEt1ciuO/EnoUPtdeGUGLZBfZiTEkXoxBc2Yh/6kBLgySDydD+Ddaa1gIYuMpM3RQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-folder/-/uui-symbol-folder-1.15.0.tgz", + "integrity": "sha512-CPsr1K5IkdxBgID+xoIcgbumm/z0q+Z/1NPxTO40EL7kx3KOLQ8vwLdOTSW1cTj90JFA9+XuRtOpmMEY0XjICg==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-symbol-lock": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-lock/-/uui-symbol-lock-1.15.0-rc.0.tgz", - "integrity": "sha512-U0hU0MAZGA6eYz5lMxKajwsEQnUGFGaDgeE6cnJBBManrlT24RmDSFmr7MlW2V0yTYOrWJOCv/P/RPFvZBNuXA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-lock/-/uui-symbol-lock-1.15.0.tgz", + "integrity": "sha512-5QyDNFjiBeuPgalT9zwPMP220zJUHPpbPvCohWCFLn/2JJsa6IjSMtsAcqxI154ZJ9vYX7vYiYUn8tJTY8CHpA==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-symbol-more": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-more/-/uui-symbol-more-1.15.0-rc.0.tgz", - "integrity": "sha512-BJ264C8xpe92a9Rx2sDirpS1jqn/en3oVGUV4wDH/BJw+jQAgTt12LzHEjQbNNEbuQBYuZuzLnoW9UVQwyV5hg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-more/-/uui-symbol-more-1.15.0.tgz", + "integrity": "sha512-BQq7BwZ7nCcgKE5tMhG6OVYTrrMEIXpx8kQKec/ULgVfs0/Ws6qeH9u4rGVK/yHU8gecd6DSeUczjjq2iS4djA==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-symbol-sort": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-sort/-/uui-symbol-sort-1.15.0-rc.0.tgz", - "integrity": "sha512-xj464OIL8Casqxk2S+SdmwBGn95k5SBh3Y93fZEUykZ4vFG2zmwk32chrJ8kX0uaQ7M3cg+lO5RZRk7QHBaPQA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-sort/-/uui-symbol-sort-1.15.0.tgz", + "integrity": "sha512-5Akw8T0SV2OrwvPk1JSeFr1clvHE4N0DwceSU9bn9f6gLIGGRxvniJAclQDRI/Woe3hm8waMy6cC2fXfSdc6lg==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-table": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-table/-/uui-table-1.15.0-rc.0.tgz", - "integrity": "sha512-usidTyVmBgE+YYt1ITg0JQxurSAyfg6bz8Fh5FLQVS9hdUWKsRvRhJP7M+u9tSIjzv07mhIlZWBvt6GJUMjFlw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-table/-/uui-table-1.15.0.tgz", + "integrity": "sha512-AnPp0QJeI70ucX8ludr3qaFmlxjKZUarX10DI8ieIB8VJiQZo0TjoPcPdSGmZupaPBLiszlpb3rKzGkZhXEIHg==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-tabs": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-tabs/-/uui-tabs-1.15.0-rc.0.tgz", - "integrity": "sha512-a5o7FXRpj3bAXTkyLlV7G7gn6nHlNn6uYEaSfwuk8sAAfcF84hp75OWLJuNrbIaH6IOblgn4FNBL01GgdAB0mg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-tabs/-/uui-tabs-1.15.0.tgz", + "integrity": "sha512-oS0eA5Z8+s+5o2ks3WCED5VGP8AunRLyuB2y7kVdRUfhCfck7B9v83zNfxPVoGoVsTDLtAQM1S4P8SHwNRmk7g==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-button": "1.15.0-rc.0", - "@umbraco-ui/uui-popover-container": "1.15.0-rc.0", - "@umbraco-ui/uui-symbol-more": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-button": "1.15.0", + "@umbraco-ui/uui-popover-container": "1.15.0", + "@umbraco-ui/uui-symbol-more": "1.15.0" } }, "node_modules/@umbraco-ui/uui-tag": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-tag/-/uui-tag-1.15.0-rc.0.tgz", - "integrity": "sha512-cnOEKEGM5BlpDEx4/Fz2o0D2n4o7si87A9UHMPbiDKFNFcJ0gfitwn7eJGn0EZs8C2GLti9wvxv24oUDJ5Hd4Q==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-tag/-/uui-tag-1.15.0.tgz", + "integrity": "sha512-PgyZvAiOZmXmiRW4UhfD6Tybx3ft755aKAVqT8ELpskLSvVr1oz4uTI6+QxoeQ1AkrHovenvIdBX+Iwi91SheQ==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-textarea": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-textarea/-/uui-textarea-1.15.0-rc.0.tgz", - "integrity": "sha512-J7+CFi16wJWMYpQ+c4VLQdA/bpDAIt6USCzoT+Rs+UYFcJKOHI4eEBxQknA61SZMbhcn9hXHI2FrIeR+2/S6GQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-textarea/-/uui-textarea-1.15.0.tgz", + "integrity": "sha512-tk/RVzCxs+KPSJ+qH2Xlr9RYxcdrSNulDKk5sBCQR0A9nwOffa15SGreSMKWgq+gYOVYChHBg/WxLWLq3d7Rlg==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@umbraco-ui/uui-toast-notification": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification/-/uui-toast-notification-1.15.0-rc.0.tgz", - "integrity": "sha512-dEu934tASMh4vbuzy11V7WIKrx5iMQ92j/1lw0ZHaDLHNdkpmkZtbSUKYlO66Wq0EMu3s9O5CAWRNFZJlV15EQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification/-/uui-toast-notification-1.15.0.tgz", + "integrity": "sha512-nz+snpjPFE+ftH5R/ekgZYy9ofGAf51yQYjWCtBwkrQ6D1dIBYA6kynZFdqIefrRwJJ5zHpe25BcS/AyRPc/9Q==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-button": "1.15.0-rc.0", - "@umbraco-ui/uui-css": "1.15.0-rc.0", - "@umbraco-ui/uui-icon": "1.15.0-rc.0", - "@umbraco-ui/uui-icon-registry-essential": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-button": "1.15.0", + "@umbraco-ui/uui-css": "1.15.0", + "@umbraco-ui/uui-icon": "1.15.0", + "@umbraco-ui/uui-icon-registry-essential": "1.15.0" } }, "node_modules/@umbraco-ui/uui-toast-notification-container": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification-container/-/uui-toast-notification-container-1.15.0-rc.0.tgz", - "integrity": "sha512-8fSSsBwAoflq7w9qwcKSmrnq6f5JADgzquEQWrK4RXvfvBeFXXlII14WbfYqIe3+FX4310VuPooHse1vd0UiCw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification-container/-/uui-toast-notification-container-1.15.0.tgz", + "integrity": "sha512-fd5d0DU/x2+u15rP0wrjw29M0oqsDFmnAfbPEdgQoPV+hvq9/SLhxJtzx10ZSNXoyuO9sTK50Q7nYsqOvGCzqg==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-toast-notification": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-toast-notification": "1.15.0" } }, "node_modules/@umbraco-ui/uui-toast-notification-layout": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification-layout/-/uui-toast-notification-layout-1.15.0-rc.0.tgz", - "integrity": "sha512-IX4w00PwV8BMbLeqs37Yn0CGGHtGlabMxwODtICze5TkPdD64Xc9W4oPbQ/Fr5CgJdiJhb6OyeVd92cr8n2lAg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification-layout/-/uui-toast-notification-layout-1.15.0.tgz", + "integrity": "sha512-uf/e/dVN6kqX76vcawiQM3w1nMHa8A+ZTtNwxtmAZi8bNPwjXLNaqKfeSp2thTByCIzFz7imnft56QtYLbksOA==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-css": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-css": "1.15.0" } }, "node_modules/@umbraco-ui/uui-toggle": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toggle/-/uui-toggle-1.15.0-rc.0.tgz", - "integrity": "sha512-ReMVDGO303IIM34HPArTIcVSCfACW8+4gY+fJr46vdy+XPjeZupoBDbxVrn+zMacxxF92sv7gqdabMYhBH6k4g==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toggle/-/uui-toggle-1.15.0.tgz", + "integrity": "sha512-WLooENcxuAobbXxN1W2uKGh/cN9k0f3cRmDDtCZdgjeheGlBYWatkc5HQte7zchXHUi0xTrsvBCBa9CsLKN/3Q==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0", - "@umbraco-ui/uui-boolean-input": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0", + "@umbraco-ui/uui-boolean-input": "1.15.0" } }, "node_modules/@umbraco-ui/uui-visually-hidden": { - "version": "1.15.0-rc.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-visually-hidden/-/uui-visually-hidden-1.15.0-rc.0.tgz", - "integrity": "sha512-1em6yB9KuZbe14ZQrwmL20YCuEeGKNHiJ4veZ5K/Dm/x9wvsd5skw5M7vMVK+gF77dJBDuGpPLtCtp/FqgFG8A==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-visually-hidden/-/uui-visually-hidden-1.15.0.tgz", + "integrity": "sha512-vn3dbpYGekAqG944Vkwd0ILQRtTaZtL1BVdsge2UsU8sOsEKwv5YzQal4b+o8yu8nb4vZbWHZ2zRmnpnPgPmjg==", "license": "MIT", "dependencies": { - "@umbraco-ui/uui-base": "1.15.0-rc.0" + "@umbraco-ui/uui-base": "1.15.0" } }, "node_modules/@vitest/expect": { @@ -5081,7 +5124,6 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -5144,7 +5186,6 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5768,6 +5809,21 @@ "node": "*" } }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -5782,7 +5838,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/c12/-/c12-2.0.1.tgz", "integrity": "sha512-Z4JgsKXHG37C6PYUtIxCfLJZvo6FyhHJoClwwb9ftUkLpPSkuYqn6Tr+vnaN8hymm0kIbcg6Ey3kv/Q71k5w/A==", - "dev": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.1", @@ -6031,7 +6086,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -6047,7 +6101,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -6090,7 +6143,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", - "dev": true, "license": "MIT", "dependencies": { "consola": "^3.2.3" @@ -6227,7 +6279,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, "license": "ISC", "bin": { "color-support": "bin.js" @@ -6295,7 +6346,6 @@ "version": "13.0.0", "resolved": "https://registry.npmjs.org/commander/-/commander-13.0.0.tgz", "integrity": "sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -6329,14 +6379,12 @@ "version": "0.1.8", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "dev": true, "license": "MIT" }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "dev": true, "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" @@ -6474,13 +6522,13 @@ } }, "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", "dev": true, "license": "MIT", "dependencies": { - "mdn-data": "2.0.30", + "mdn-data": "2.12.2", "source-map-js": "^1.0.1" }, "engines": { @@ -6782,6 +6830,34 @@ "node": ">=0.10.0" } }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/default-gateway": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", @@ -6868,7 +6944,6 @@ "version": "6.1.4", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", - "dev": true, "license": "MIT" }, "node_modules/degenerator": { @@ -6956,7 +7031,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "dev": true, "license": "MIT" }, "node_modules/destroy": { @@ -7247,7 +7321,6 @@ "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -8452,7 +8525,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -8465,7 +8537,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -8478,7 +8549,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, "license": "ISC" }, "node_modules/fs.realpath": { @@ -8687,7 +8757,6 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/giget/-/giget-1.2.5.tgz", "integrity": "sha512-r1ekGw/Bgpi3HLV3h1MRBIlSAdHoIMklpaQ3OQLFcRw9PwAj2rqigvIbg+dBUI51OxVI2jsEtDywDBjSiuf7Ug==", - "dev": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -8706,7 +8775,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, "license": "MIT" }, "node_modules/glob": { @@ -8866,7 +8934,6 @@ "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, "license": "MIT", "dependencies": { "minimist": "^1.2.5", @@ -8888,7 +8955,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -9538,6 +9604,39 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", @@ -9952,7 +10051,6 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", - "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -9986,7 +10084,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -10314,7 +10411,6 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, "license": "MIT" }, "node_modules/lodash.camelcase": { @@ -10410,9 +10506,9 @@ } }, "node_modules/lucide-static": { - "version": "0.539.0", - "resolved": "https://registry.npmjs.org/lucide-static/-/lucide-static-0.539.0.tgz", - "integrity": "sha512-ybhtBJOeIgY1d1WV3vxJkSOoWi1EaYOkA15WYtct9/kX3+HSTCdbkeM0fQ4i5JxsKmp874J8dsNI92r4t5qCzg==", + "version": "0.542.0", + "resolved": "https://registry.npmjs.org/lucide-static/-/lucide-static-0.542.0.tgz", + "integrity": "sha512-jnXxFQYH0F24Nj5Zjje5jmMbGjClohnuSEYwTjAGuAa7UYDQiUjl0H7t4Ha19lkKryhCQA3VfeAJMjarrhzNZQ==", "dev": true, "license": "ISC" }, @@ -10789,9 +10885,9 @@ } }, "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", "dev": true, "license": "CC0-1.0" }, @@ -11493,7 +11589,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -11503,7 +11598,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=8" @@ -11513,7 +11607,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, "license": "MIT", "dependencies": { "minipass": "^3.0.0", @@ -11527,7 +11620,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -11540,7 +11632,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, "license": "ISC" }, "node_modules/mitt": { @@ -11554,7 +11645,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" @@ -11567,7 +11657,6 @@ "version": "1.7.4", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", - "dev": true, "license": "MIT", "dependencies": { "acorn": "^8.14.0", @@ -11580,7 +11669,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, "license": "MIT" }, "node_modules/module-definition": { @@ -11821,7 +11909,6 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, "license": "MIT" }, "node_modules/netmask": { @@ -11858,7 +11945,6 @@ "version": "1.6.7", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "dev": true, "license": "MIT" }, "node_modules/node-fetch/node_modules/tr46": { @@ -11943,7 +12029,6 @@ "version": "0.5.4", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.5.4.tgz", "integrity": "sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA==", - "dev": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -11964,7 +12049,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, "license": "MIT" }, "node_modules/object-inspect": { @@ -12068,7 +12152,6 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.6.tgz", "integrity": "sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg==", - "dev": true, "license": "MIT" }, "node_modules/on-finished": { @@ -12457,7 +12540,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true, "license": "MIT" }, "node_modules/pathval": { @@ -12481,7 +12563,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", - "dev": true, "license": "MIT" }, "node_modules/picocolors": { @@ -12518,7 +12599,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", - "dev": true, "license": "MIT", "dependencies": { "confbox": "^0.1.8", @@ -12530,7 +12610,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, "license": "MIT" }, "node_modules/playwright": { @@ -13282,63 +13361,6 @@ "postcss": "^8.4.32" } }, - "node_modules/postcss-svgo/node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - } - }, - "node_modules/postcss-svgo/node_modules/css-tree": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", - "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "mdn-data": "2.12.2", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/postcss-svgo/node_modules/mdn-data": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", - "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/postcss-svgo/node_modules/svgo": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz", - "integrity": "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "commander": "^11.1.0", - "css-select": "^5.1.0", - "css-tree": "^3.0.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.1.1", - "sax": "^1.4.1" - }, - "bin": { - "svgo": "bin/svgo.js" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, "node_modules/postcss-unique-selectors": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.4.tgz", @@ -13954,7 +13976,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", - "dev": true, "license": "MIT", "dependencies": { "defu": "^6.1.4", @@ -14013,7 +14034,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -14393,6 +14413,18 @@ "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==", "license": "MIT" }, + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -14749,17 +14781,23 @@ "license": "ISC" }, "node_modules/simple-icons": { - "version": "14.15.0", - "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-14.15.0.tgz", - "integrity": "sha512-yjlJ8skP4isCd4dQTDGFbv0hQgDIr57DbWaovEeHADPAZa5DBEAs+3ZnBb76ti+yz7/OzFI3SqTP1SKku1uhCw==", + "version": "15.13.0", + "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-15.13.0.tgz", + "integrity": "sha512-Tk3p1Lgmji+8kdUOIvvU3f0loYtWVo/bvO1T82Whpb6BhQ+wM2JR8BaVLQT+TiqqF25DVGrVYXeXrsPo6YDOvg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/simple-icons" + }, + { + "type": "github", + "url": "https://github.com/sponsors/simple-icons" + } + ], "license": "CC0-1.0", "engines": { "node": ">=0.12.18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/simple-icons" } }, "node_modules/slash": { @@ -15229,25 +15267,25 @@ } }, "node_modules/svgo": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", - "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz", + "integrity": "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==", "dev": true, "license": "MIT", "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", + "commander": "^11.1.0", "css-select": "^5.1.0", - "css-tree": "^2.3.1", + "css-tree": "^3.0.1", "css-what": "^6.1.0", "csso": "^5.0.5", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1", + "sax": "^1.4.1" }, "bin": { - "svgo": "bin/svgo" + "svgo": "bin/svgo.js" }, "engines": { - "node": ">=14.0.0" + "node": ">=16" }, "funding": { "type": "opencollective", @@ -15255,13 +15293,13 @@ } }, "node_modules/svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=16" } }, "node_modules/synckit": { @@ -15318,7 +15356,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dev": true, "license": "ISC", "dependencies": { "chownr": "^2.0.0", @@ -15363,7 +15400,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, "license": "ISC" }, "node_modules/text-decoder": { @@ -15412,7 +15448,6 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, "license": "MIT" }, "node_modules/tinyglobby": { @@ -16086,14 +16121,12 @@ "version": "1.6.1", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", - "dev": true, "license": "MIT" }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, "license": "BSD-2-Clause", "optional": true, "bin": { @@ -16390,14 +16423,14 @@ } }, "node_modules/vite": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.2.tgz", - "integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz", + "integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.6", + "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", @@ -16803,7 +16836,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, "license": "MIT" }, "node_modules/wordwrapjs": { @@ -17032,17 +17064,36 @@ "name": "@umbraco-backoffice/external-tiptap", "dependencies": { "@tiptap/core": "2.26.1", + "@tiptap/extension-blockquote": "2.26.1", + "@tiptap/extension-bold": "2.26.1", + "@tiptap/extension-bullet-list": "2.26.1", "@tiptap/extension-character-count": "2.26.1", + "@tiptap/extension-code": "2.26.1", + "@tiptap/extension-code-block": "2.26.1", + "@tiptap/extension-document": "2.26.1", + "@tiptap/extension-dropcursor": "2.26.1", + "@tiptap/extension-gapcursor": "2.26.1", + "@tiptap/extension-hard-break": "2.26.1", + "@tiptap/extension-heading": "2.26.1", + "@tiptap/extension-history": "2.26.1", + "@tiptap/extension-horizontal-rule": "2.26.1", "@tiptap/extension-image": "2.26.1", + "@tiptap/extension-italic": "2.26.1", "@tiptap/extension-link": "2.26.1", + "@tiptap/extension-list-item": "2.26.1", + "@tiptap/extension-ordered-list": "2.26.1", + "@tiptap/extension-paragraph": "2.26.1", "@tiptap/extension-placeholder": "2.26.1", + "@tiptap/extension-strike": "2.26.1", "@tiptap/extension-subscript": "2.26.1", "@tiptap/extension-superscript": "2.26.1", "@tiptap/extension-table": "2.26.1", "@tiptap/extension-table-cell": "2.26.1", "@tiptap/extension-table-header": "2.26.1", "@tiptap/extension-table-row": "2.26.1", + "@tiptap/extension-text": "2.26.1", "@tiptap/extension-text-align": "2.26.1", + "@tiptap/extension-text-style": "2.26.1", "@tiptap/extension-underline": "2.26.1", "@tiptap/pm": "2.26.1", "@tiptap/starter-kit": "2.26.1" @@ -17051,8 +17102,8 @@ "src/external/uui": { "name": "@umbraco-backoffice/uui", "dependencies": { - "@umbraco-ui/uui": "^1.15.0-rc.0", - "@umbraco-ui/uui-css": "^1.15.0-rc.0" + "@umbraco-ui/uui": "^1.15.0", + "@umbraco-ui/uui-css": "^1.15.0" } }, "src/packages/block": { @@ -17070,23 +17121,15 @@ "src/packages/core": { "name": "@umbraco-backoffice/core", "dependencies": { - "@hey-api/client-fetch": "^0.12.0", + "@hey-api/openapi-ts": "0.81.1", "@types/diff": "^7.0.2", "diff": "^7.0.0", "uuid": "^11.1.0" }, "devDependencies": { - "@hey-api/openapi-ts": "^0.71.0" - } - }, - "src/packages/core/node_modules/@hey-api/client-fetch": { - "version": "0.12.0", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/hey-api" - }, - "peerDependencies": { - "@hey-api/openapi-ts": "< 2" + "lucide-static": "^0.542.0", + "simple-icons": "^15.13.0", + "svgo": "^4.0.0" } }, "src/packages/core/node_modules/diff": { diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index adb301a6ad..bd7be940dd 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -242,7 +242,6 @@ "eslint-plugin-storybook": "9.0.14", "eslint-plugin-wc": "^2.2.1", "globals": "^16.3.0", - "lucide-static": "^0.539.0", "madge": "^8.0.0", "msw": "^1.3.5", "playwright-msw": "^3.0.1", @@ -250,16 +249,14 @@ "postcss-cli": "^11.0.1", "prettier": "3.6.2", "remark-gfm": "^4.0.1", - "simple-icons": "^14.15.0", "storybook": "9.0.14", - "svgo": "^3.3.2", "tiny-glob": "^0.2.9", "tsc-alias": "^1.8.16", "typedoc": "^0.28.10", "typescript": "5.9.2", "typescript-eslint": "^8.39.1", "typescript-json-schema": "^0.65.1", - "vite": "^7.1.2", + "vite": "^7.1.3", "vite-plugin-static-copy": "^3.1.2", "vite-tsconfig-paths": "^5.1.4", "web-component-analyzer": "^2.0.0" diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts index ff655336d5..7a17520579 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/app.element.ts @@ -19,7 +19,7 @@ import { umbExtensionsRegistry, } from '@umbraco-cms/backoffice/extension-registry'; import { filter, first, firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs'; -import { hasOwnOpener, retrieveStoredPath } from '@umbraco-cms/backoffice/utils'; +import { hasOwnOpener, redirectToStoredPath } from '@umbraco-cms/backoffice/utils'; import { UmbApiInterceptorController } from '@umbraco-cms/backoffice/resources'; import { umbHttpClient } from '@umbraco-cms/backoffice/http-client'; @@ -61,7 +61,7 @@ export class UmbAppElement extends UmbLitElement { { path: 'oauth_complete', component: () => import('./app-oauth.element.js'), - setup: (component) => { + setup: async (component) => { if (!this.#authContext) { (component as UmbAppOauthElement).failure = true; console.error('[Fatal] Auth context is not available'); @@ -76,26 +76,37 @@ export class UmbAppElement extends UmbLitElement { return; } - // If we are in the main window (i.e. no opener), we should redirect to the root after the authorization request is completed. - // The authorization request will be completed in the active window (main or popup) and the authorization signal will be sent. - // If we are in a popup window, the storage event in UmbAuthContext will catch the signal and close the window. - // If we are in the main window, the signal will be caught right here and the user will be redirected to the root. - if (!hasOwnOpener(this.backofficePath)) { - this.observe(this.#authContext.authorizationSignal, () => { - // Redirect to the saved state or root - const url = retrieveStoredPath(); - const isBackofficePath = url?.pathname.startsWith(this.backofficePath) ?? true; - - if (isBackofficePath) { - history.replaceState(null, '', url?.toString() ?? ''); - } else { - window.location.href = url?.toString() ?? this.backofficePath; - } - }); + // Check that we are not already authorized + if (this.#authContext.getIsAuthorized()) { + redirectToStoredPath(this.backofficePath, true); + return; } // Complete the authorization request, which will send the authorization signal - this.#authContext.completeAuthorizationRequest().catch(() => undefined); + try { + const result = await this.#authContext.completeAuthorizationRequest(); + + if (result === null) { + // If the result is null, it could mean that no new token was required, so we can redirect the user + // This could happen if the user is already authorized or accidentally enters the oauth_complete url + redirectToStoredPath(this.backofficePath, true); + return; + } + + // If we are in the main window (i.e. no opener), we should redirect to the root after the authorization request is completed. + // The authorization request will be completed in the active window (main or popup) and the authorization signal will be sent. + // If we are in a popup window, the storage event in UmbAuthContext will catch the signal and close the window. + // If we are in the main window, the signal will be caught right here and the user will be redirected to their previous path (or root). + if (hasOwnOpener(this.backofficePath)) return; + + // Listen for the first authorization signal after the initial authorization request + await firstValueFrom(this.#authContext.authorizationSignal); + // When it hits, we should redirect the user to the backoffice + redirectToStoredPath(this.backofficePath); + } catch { + (component as UmbAppOauthElement).failure = true; + console.error('[Fatal] Authorization request failed'); + } }, }, { diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-main.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-main.element.ts index 0550e6c52c..70f1a9202b 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-main.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-main.element.ts @@ -78,6 +78,7 @@ export class UmbBackofficeMainElement extends UmbLitElement { this._routes = newRoutes; } + // TODO: v17. Remove this section context when we have api's as part of the section manifest. private _provideSectionContext(sectionManifest: ManifestSection) { if (!this._sectionContext) { this._sectionContext = new UmbSectionContext(this); diff --git a/src/Umbraco.Web.UI.Client/src/apps/installer/installer.context.ts b/src/Umbraco.Web.UI.Client/src/apps/installer/installer.context.ts index b638c1d94e..500922d9ab 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/installer/installer.context.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/installer/installer.context.ts @@ -1,8 +1,5 @@ import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; -import type { - InstallSettingsResponseModel, - InstallRequestModelReadable, -} from '@umbraco-cms/backoffice/external/backend-api'; +import type { InstallSettingsResponseModel, InstallRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; import { InstallService, TelemetryLevelModel } from '@umbraco-cms/backoffice/external/backend-api'; import { tryExecute, UmbApiError, type UmbProblemDetails } from '@umbraco-cms/backoffice/resources'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; @@ -15,7 +12,7 @@ import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; * @class UmbInstallerContext */ export class UmbInstallerContext extends UmbContextBase { - private _data = new UmbObjectState({ + private _data = new UmbObjectState({ user: { name: '', email: '', password: '', subscribeToNewsletter: false }, database: { id: '', providerName: '', useIntegratedAuthentication: false, trustServerCertificate: false }, telemetryLevel: TelemetryLevelModel.DETAILED, @@ -90,7 +87,7 @@ export class UmbInstallerContext extends UmbContextBase { * @param {Partial} data The data to set * @memberof UmbInstallerContext */ - public appendData(data: Partial): void { + public appendData(data: Partial): void { this._data.setValue({ ...this.getData(), ...data }); } @@ -100,7 +97,7 @@ export class UmbInstallerContext extends UmbContextBase { * @returns {*} {PostInstallRequest} * @memberof UmbInstallerContext */ - public getData(): InstallRequestModelReadable { + public getData(): InstallRequestModel { return this._data.getValue(); } diff --git a/src/Umbraco.Web.UI.Client/src/apps/upgrader/upgrader-view.element.ts b/src/Umbraco.Web.UI.Client/src/apps/upgrader/upgrader-view.element.ts index 21fc60d6db..8ea5de2147 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/upgrader/upgrader-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/upgrader/upgrader-view.element.ts @@ -1,6 +1,6 @@ import type { CSSResultGroup } from '@umbraco-cms/backoffice/external/lit'; import { css, html, LitElement, customElement, property, ifDefined } from '@umbraco-cms/backoffice/external/lit'; -import type { UpgradeSettingsResponseModelReadable } from '@umbraco-cms/backoffice/external/backend-api'; +import type { UpgradeSettingsResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; /** * @element umb-upgrader-view @@ -18,7 +18,7 @@ export class UmbUpgraderViewElement extends LitElement { errorMessage = ''; @property({ type: Object, reflect: true }) - settings?: UpgradeSettingsResponseModelReadable; + settings?: UpgradeSettingsResponseModel; private _renderLayout() { return html` diff --git a/src/Umbraco.Web.UI.Client/src/apps/upgrader/upgrader.element.ts b/src/Umbraco.Web.UI.Client/src/apps/upgrader/upgrader.element.ts index fa1b105c48..5107af5c8d 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/upgrader/upgrader.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/upgrader/upgrader.element.ts @@ -1,5 +1,5 @@ import { html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; -import type { UpgradeSettingsResponseModelReadable } from '@umbraco-cms/backoffice/external/backend-api'; +import type { UpgradeSettingsResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { UpgradeService } from '@umbraco-cms/backoffice/external/backend-api'; import { tryExecute, UmbApiError } from '@umbraco-cms/backoffice/resources'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; @@ -13,7 +13,7 @@ import './upgrader-view.element.js'; @customElement('umb-upgrader') export class UmbUpgraderElement extends UmbLitElement { @state() - private _upgradeSettings?: UpgradeSettingsResponseModelReadable; + private _upgradeSettings?: UpgradeSettingsResponseModel; @state() private _fetching = true; diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/da.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/da.ts index 3778a262ec..f82d1861c6 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/da.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/da.ts @@ -176,6 +176,9 @@ export default { confirmActionConfirm: 'Bekræft', morePublishingOptions: 'Flere publiseringsmuligheder', submitChanges: 'Indsæt', + openInSplitView: 'Åbn i sidestillet visning', + openVersionSelector: 'Åbn udgavevælger', + closeVersionSelector: 'Luk udgavevælger', }, auditTrailsMedia: { delete: 'Medie slettet', @@ -352,6 +355,7 @@ export default { selectAllVariants: 'Vælg alle varianter', scheduledPendingChanges: 'Denne tidsplan har ændringer, der træder i kraft, når du klikker på "%0%".', noVariantsToProcess: 'Der er ingen tilgængelige varianter', + openSplitViewForVariant: (variant: string) => `Åbn ${variant} i sidestillet visning`, }, blueprints: { createBlueprintFrom: "Opret en ny indholdsskabelon fra '%0%'", diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts index ee8ce07208..66cc794032 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/en.ts @@ -177,6 +177,9 @@ export default { morePublishingOptions: 'More publishing options', submitChanges: 'Submit', viewSystemDetails: 'View Umbraco CMS system information and version number', + openInSplitView: 'Open in split view', + openVersionSelector: 'Open version selector', + closeVersionSelector: 'Close version selector', }, auditTrailsMedia: { delete: 'Media deleted', @@ -353,6 +356,7 @@ export default { saveModalTitle: 'Save', saveAndPublishModalTitle: 'Save and publish', publishModalTitle: 'Publish', + openSplitViewForVariant: (variant: string) => `Open ${variant} in split view`, }, blueprints: { createBlueprintFrom: "Create a new Document Blueprint from '%0%'", @@ -2788,6 +2792,7 @@ export default { config_statusbar: 'Statusbar', config_toolbar: 'Toolbar', extGroup_formatting: 'Text formatting', + extGroup_html: 'HTML', extGroup_interactive: 'Interactive elements', extGroup_media: 'Embeds and media', extGroup_structure: 'Content structure', diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/es.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/es.ts index bc6f603820..deee492da0 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/es.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/es.ts @@ -204,6 +204,7 @@ export default { removeTextBox: 'Eliminar caja de texto', contentRoot: 'Raíz de contenido', saveModalTitle: 'Guardar', + openSplitViewForVariant: (variant: string) => `Abrir vista dividida para '${variant}'`, }, blueprints: { createBlueprintFrom: 'Crear nueva Plantilla de Contenido desde %0%', diff --git a/src/Umbraco.Web.UI.Client/src/external/openid/src/authorization_request_handler.ts b/src/Umbraco.Web.UI.Client/src/external/openid/src/authorization_request_handler.ts index 90eb72b171..d77d8a0e59 100644 --- a/src/Umbraco.Web.UI.Client/src/external/openid/src/authorization_request_handler.ts +++ b/src/Umbraco.Web.UI.Client/src/external/openid/src/authorization_request_handler.ts @@ -118,7 +118,7 @@ export abstract class AuthorizationRequestHandler { /** * Completes the authorization request if necessary & when possible. */ - completeAuthorizationRequestIfPossible(): Promise { + completeAuthorizationRequestIfPossible(): Promise { // call complete authorization if possible to see there might // be a response that needs to be delivered. log(`Checking to see if there is an authorization response to be delivered.`); @@ -133,6 +133,7 @@ export abstract class AuthorizationRequestHandler { if (result && this.notifier) { this.notifier.onAuthorizationComplete(result.request, result.response, result.error); } + return result; }); } diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-anchor.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-anchor.extension.ts index 12c5a2b0ec..fda5a77bfc 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-anchor.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-anchor.extension.ts @@ -1,5 +1,6 @@ import { Node, mergeAttributes } from '@tiptap/core'; +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const Anchor = Node.create({ name: 'anchor', diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-div.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-div.extension.ts index e7855d4288..374acf2557 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-div.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-div.extension.ts @@ -1,5 +1,6 @@ import { Node, mergeAttributes } from '@tiptap/core'; +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export interface DivOptions { /** * HTML attributes to add to the element. @@ -9,6 +10,7 @@ export interface DivOptions { HTMLAttributes: Record; } +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const Div = Node.create({ name: 'div', diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-figcaption.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-figcaption.extension.ts index ea2f2ab83e..8a1bce8d40 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-figcaption.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-figcaption.extension.ts @@ -1,5 +1,6 @@ import { Node } from '@tiptap/core'; +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export interface FigcaptionOptions { /** * HTML attributes to add to the image element. @@ -9,6 +10,7 @@ export interface FigcaptionOptions { HTMLAttributes: Record; } +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const Figcaption = Node.create({ name: 'figcaption', diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-figure.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-figure.extension.ts index 49a89606df..4e4b67a308 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-figure.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-figure.extension.ts @@ -1,5 +1,6 @@ import { mergeAttributes, Node } from '@tiptap/core'; +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export interface FigureOptions { /** * HTML attributes to add to the image element. @@ -9,6 +10,7 @@ export interface FigureOptions { HTMLAttributes: Record; } +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const Figure = Node.create({ name: 'figure', group: 'block', diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-html-global-attributes.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-html-global-attributes.extension.ts index 69c0cbdd1f..06454f82b9 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-html-global-attributes.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-html-global-attributes.extension.ts @@ -10,6 +10,7 @@ function camelCaseToKebabCase(str: string): string { return str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, ($, ofs) => (ofs ? '-' : '') + $.toLowerCase()); } +/** @deprecated No longer used internally. This will be removed in Umbraco 17. [LK] */ export interface HtmlGlobalAttributesOptions { /** * The types where the text align attribute can be applied. @@ -19,6 +20,7 @@ export interface HtmlGlobalAttributesOptions { types: Array; } +/** @deprecated No longer used internally. This will be removed in Umbraco 17. [LK] */ export const HtmlGlobalAttributes = Extension.create({ name: 'htmlGlobalAttributes', diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-span.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-span.extension.ts index e0adabf8af..bee0cd8632 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-span.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-span.extension.ts @@ -1,5 +1,6 @@ import { Mark, mergeAttributes } from '@tiptap/core'; +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export interface SpanOptions { /** * HTML attributes to add to the span element. @@ -34,6 +35,7 @@ function serializeStyles(items: Record): string { ); } +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const Span = Mark.create({ name: 'span', diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-text-direction-extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-text-direction-extension.ts index 914d151cd1..96727e9dae 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-text-direction-extension.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-text-direction-extension.ts @@ -1,10 +1,12 @@ import { Extension } from '@tiptap/core'; +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export interface TextDirectionOptions { directions: Array<'auto' | 'ltr' | 'rtl'>; types: Array; } +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const TextDirection = Extension.create({ name: 'textDirection', diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-text-indent-extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-text-indent-extension.ts index 7df5d4043e..a39c10605d 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-text-indent-extension.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-text-indent-extension.ts @@ -11,12 +11,14 @@ import type { EditorState, Transaction } from '@tiptap/pm/state'; import { Extension } from '@tiptap/core'; import { AllSelection, TextSelection } from '@tiptap/pm/state'; +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export interface TextIndentOptions { minLevel: number; maxLevel: number; types: Array; } +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const TextIndent = Extension.create({ name: 'textIndent', diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-trailing-node.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-trailing-node.extension.ts index f76956fa90..ec7f4144f1 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-trailing-node.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-trailing-node.extension.ts @@ -13,11 +13,13 @@ function nodeEqualsType({ types, node }) { return (Array.isArray(types) && types.includes(node.type)) || node.type === types; } +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export interface TrailingNodeOptions { node: string; notAfter: string[]; } +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const TrailingNode = Extension.create({ name: 'trailingNode', diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-bubble-menu.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-bubble-menu.extension.ts index f2b080c42e..a34064ae9c 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-bubble-menu.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-bubble-menu.extension.ts @@ -5,11 +5,13 @@ import { EditorState, Plugin, PluginKey } from '@tiptap/pm/state'; import { EditorView } from '@tiptap/pm/view'; import type { PluginView } from '@tiptap/pm/state'; +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export interface UmbTiptapBubbleMenuElement extends HTMLElement { editor?: Editor; menuAlias?: string; } +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export type UmbBubbleMenuPluginProps = { unique: string; placement?: UUIPopoverContainerElement['placement']; @@ -20,8 +22,10 @@ export type UmbBubbleMenuPluginProps = { | null; }; +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export type UmbBubbleMenuOptions = UmbBubbleMenuPluginProps; +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const UmbBubbleMenu = Extension.create({ name: 'umbBubbleMenu', @@ -104,6 +108,7 @@ class UmbBubbleMenuPluginView implements PluginView { } } +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const UmbBubbleMenuPlugin = (editor: Editor, props: UmbBubbleMenuPluginProps) => { return new Plugin({ view(editorView) { diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts index daecec2468..fce7c67a48 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-embedded-media.extension.ts @@ -1,9 +1,11 @@ import { mergeAttributes, Node } from '@tiptap/core'; +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export interface UmbEmbeddedMediaOptions { inline: boolean; } +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const umbEmbeddedMedia = Node.create({ name: 'umbEmbeddedMedia', group() { diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts index 84acd5b11d..1f91f7e958 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-image.extension.ts @@ -1,5 +1,6 @@ import Image from '@tiptap/extension-image'; +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export interface UmbImageAttributes { src: string; alt?: string; @@ -13,6 +14,7 @@ export interface UmbImageAttributes { 'data-udi'?: string; } +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const UmbImage = Image.extend({ addAttributes() { return { diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-link.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-link.extension.ts index 9ba9433993..ef7e66617e 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-link.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-link.extension.ts @@ -1,5 +1,6 @@ import Link from '@tiptap/extension-link'; +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const UmbLink = Link.extend({ name: 'umbLink', @@ -12,6 +13,9 @@ export const UmbLink = Link.extend({ }; }, + // TODO: [LK] Look to use a NodeView to render the link + // https://tiptap.dev/docs/editor/extensions/custom-extensions/node-views/javascript + addOptions() { return { ...this.parent?.(), diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-table.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-table.extension.ts index df5ec8a8fb..a06850f076 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-table.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-table.extension.ts @@ -12,6 +12,7 @@ import type { Rect } from '@tiptap/pm/tables'; // NOTE: Custom TableView, to allow for custom styles to be applied to the element. [LK] // ref: https://github.com/ueberdosis/tiptap/blob/v2.11.5/packages/extension-table/src/TableView.ts +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export class UmbTableView extends TableView { constructor(node: ProseMirrorNode, cellMinWidth: number) { super(node, cellMinWidth); @@ -34,13 +35,16 @@ export class UmbTableView extends TableView { } } +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const UmbTable = Table.configure({ resizable: true, View: UmbTableView }); +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const UmbTableRow = TableRow.extend({ allowGapCursor: false, content: '(tableCell | tableHeader)*', }); +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const UmbTableHeader = TableHeader.extend({ addAttributes() { return { @@ -122,6 +126,7 @@ export const UmbTableHeader = TableHeader.extend({ }, }); +/** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ export const UmbTableCell = TableCell.extend({ addAttributes() { return { diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts index 9bb1aedd28..1cfd0f26bd 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts @@ -1,32 +1,134 @@ // REQUIRED EXTENSIONS export * from '@tiptap/core'; -export { StarterKit } from '@tiptap/starter-kit'; -export { Placeholder } from '@tiptap/extension-placeholder'; -export { TextStyle } from '@tiptap/extension-text-style'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Document, +} from '@tiptap/extension-document'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Dropcursor, +} from '@tiptap/extension-dropcursor'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Gapcursor, +} from '@tiptap/extension-gapcursor'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + HardBreak, +} from '@tiptap/extension-hard-break'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + History, +} from '@tiptap/extension-history'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Paragraph, +} from '@tiptap/extension-paragraph'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Placeholder, +} from '@tiptap/extension-placeholder'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Text, +} from '@tiptap/extension-text'; // OPTIONAL EXTENSIONS -export { Blockquote } from '@tiptap/extension-blockquote'; -export { Bold } from '@tiptap/extension-bold'; -export { BulletList } from '@tiptap/extension-bullet-list'; -export { CharacterCount } from '@tiptap/extension-character-count'; -export { Code } from '@tiptap/extension-code'; -export { CodeBlock } from '@tiptap/extension-code-block'; -export { Heading } from '@tiptap/extension-heading'; -export { HorizontalRule } from '@tiptap/extension-horizontal-rule'; -export { Image } from '@tiptap/extension-image'; -export { Italic } from '@tiptap/extension-italic'; -export { Link } from '@tiptap/extension-link'; -export { ListItem } from '@tiptap/extension-list-item'; -export { OrderedList } from '@tiptap/extension-ordered-list'; -export { Strike } from '@tiptap/extension-strike'; -export { Subscript } from '@tiptap/extension-subscript'; -export { Superscript } from '@tiptap/extension-superscript'; -export { Table } from '@tiptap/extension-table'; -export { TableCell } from '@tiptap/extension-table-cell'; -export { TableHeader } from '@tiptap/extension-table-header'; -export { TableRow } from '@tiptap/extension-table-row'; -export { TextAlign } from '@tiptap/extension-text-align'; -export { Underline } from '@tiptap/extension-underline'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Blockquote, +} from '@tiptap/extension-blockquote'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Bold, +} from '@tiptap/extension-bold'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + BulletList, +} from '@tiptap/extension-bullet-list'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + CharacterCount, +} from '@tiptap/extension-character-count'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Code, +} from '@tiptap/extension-code'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + CodeBlock, +} from '@tiptap/extension-code-block'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Heading, +} from '@tiptap/extension-heading'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + HorizontalRule, +} from '@tiptap/extension-horizontal-rule'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Image, +} from '@tiptap/extension-image'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Italic, +} from '@tiptap/extension-italic'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Link, +} from '@tiptap/extension-link'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + ListItem, +} from '@tiptap/extension-list-item'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + OrderedList, +} from '@tiptap/extension-ordered-list'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Strike, +} from '@tiptap/extension-strike'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Subscript, +} from '@tiptap/extension-subscript'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Superscript, +} from '@tiptap/extension-superscript'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Table, +} from '@tiptap/extension-table'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + TableCell, +} from '@tiptap/extension-table-cell'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + TableHeader, +} from '@tiptap/extension-table-header'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + TableRow, +} from '@tiptap/extension-table-row'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + TextAlign, +} from '@tiptap/extension-text-align'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + TextStyle, +} from '@tiptap/extension-text-style'; +export { + /** @deprecated This will be relocated in Umbraco 17 to the "@umbraco-cms/backoffice/tiptap" module. [LK] */ + Underline, +} from '@tiptap/extension-underline'; + +/** @deprecated No longer used internally. This will be removed in Umbraco 18. [LK] */ +export { StarterKit } from '@tiptap/starter-kit'; // CUSTOM EXTENSIONS export * from './extensions/tiptap-anchor.extension.js'; diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/package.json b/src/Umbraco.Web.UI.Client/src/external/tiptap/package.json index 11938ce31a..61a34ee1c9 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/package.json +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/package.json @@ -7,10 +7,27 @@ }, "dependencies": { "@tiptap/core": "2.26.1", + "@tiptap/extension-blockquote": "2.26.1", + "@tiptap/extension-bold": "2.26.1", + "@tiptap/extension-bullet-list": "2.26.1", "@tiptap/extension-character-count": "2.26.1", + "@tiptap/extension-code-block": "2.26.1", + "@tiptap/extension-code": "2.26.1", + "@tiptap/extension-document": "2.26.1", + "@tiptap/extension-dropcursor": "2.26.1", + "@tiptap/extension-gapcursor": "2.26.1", + "@tiptap/extension-hard-break": "2.26.1", + "@tiptap/extension-heading": "2.26.1", + "@tiptap/extension-history": "2.26.1", + "@tiptap/extension-horizontal-rule": "2.26.1", "@tiptap/extension-image": "2.26.1", + "@tiptap/extension-italic": "2.26.1", "@tiptap/extension-link": "2.26.1", + "@tiptap/extension-list-item": "2.26.1", + "@tiptap/extension-ordered-list": "2.26.1", + "@tiptap/extension-paragraph": "2.26.1", "@tiptap/extension-placeholder": "2.26.1", + "@tiptap/extension-strike": "2.26.1", "@tiptap/extension-subscript": "2.26.1", "@tiptap/extension-superscript": "2.26.1", "@tiptap/extension-table": "2.26.1", @@ -18,6 +35,8 @@ "@tiptap/extension-table-header": "2.26.1", "@tiptap/extension-table-row": "2.26.1", "@tiptap/extension-text-align": "2.26.1", + "@tiptap/extension-text-style": "2.26.1", + "@tiptap/extension-text": "2.26.1", "@tiptap/extension-underline": "2.26.1", "@tiptap/pm": "2.26.1", "@tiptap/starter-kit": "2.26.1" diff --git a/src/Umbraco.Web.UI.Client/src/external/uui/package.json b/src/Umbraco.Web.UI.Client/src/external/uui/package.json index 072fb13ef4..1c3a7b4b27 100644 --- a/src/Umbraco.Web.UI.Client/src/external/uui/package.json +++ b/src/Umbraco.Web.UI.Client/src/external/uui/package.json @@ -6,7 +6,7 @@ "build": "vite build" }, "dependencies": { - "@umbraco-ui/uui": "^1.15.0-rc.0", - "@umbraco-ui/uui-css": "^1.15.0-rc.0" + "@umbraco-ui/uui": "^1.15.0", + "@umbraco-ui/uui-css": "^1.15.0" } -} +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/examine.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/examine.data.ts index 4c04edc1ea..d639085407 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/examine.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/examine.data.ts @@ -1,7 +1,7 @@ import type { IndexResponseModel, PagedIndexResponseModel, - SearchResultResponseModelReadable, + SearchResultResponseModel, } from '@umbraco-cms/backoffice/external/backend-api'; import { HealthStatusModel } from '@umbraco-cms/backoffice/external/backend-api'; @@ -19,7 +19,7 @@ export function getIndexByName(indexName: string) { /** * */ -export function getSearchResultsMockData(): SearchResultResponseModelReadable[] { +export function getSearchResultsMockData(): SearchResultResponseModel[] { return searchResultMockData; } @@ -91,7 +91,7 @@ export const PagedIndexers: PagedIndexResponseModel = { total: 0, }; -export const searchResultMockData: SearchResultResponseModelReadable[] = [ +export const searchResultMockData: SearchResultResponseModel[] = [ { id: '1', score: 1, diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/relation/relation.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/relation/relation.data.ts index be5d35b460..11a9228118 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/relation/relation.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/relation/relation.data.ts @@ -1,9 +1,6 @@ -import type { - RelationResponseModelReadable, - RelationReferenceModel, -} from '@umbraco-cms/backoffice/external/backend-api'; +import type { RelationResponseModel, RelationReferenceModel } from '@umbraco-cms/backoffice/external/backend-api'; -export type UmbMockRelationModel = RelationResponseModelReadable; +export type UmbMockRelationModel = RelationResponseModel; export type UmbMockRelationReferenceModel = RelationReferenceModel; export const data: Array = [ diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/relation/relation.db.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/relation/relation.db.ts index d885a524af..122b1506cb 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/relation/relation.db.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/relation/relation.db.ts @@ -2,7 +2,7 @@ import { UmbMockEntityDetailManager } from '../utils/entity/entity-detail.manage import { UmbEntityMockDbBase } from '../utils/entity/entity-base.js'; import type { UmbMockRelationModel } from './relation.data.js'; import { data } from './relation.data.js'; -import type { RelationResponseModelReadable } from '@umbraco-cms/backoffice/external/backend-api'; +import type { RelationResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; class UmbRelationMockDB extends UmbEntityMockDbBase { item = new UmbMockEntityDetailManager(this, itemResponseMapper, createDetailMockMapper); @@ -16,7 +16,7 @@ const createDetailMockMapper = (): UmbMockRelationModel => { throw new Error('Not possible to create a relation'); }; -const itemResponseMapper = (item: UmbMockRelationModel): RelationResponseModelReadable => { +const itemResponseMapper = (item: UmbMockRelationModel): RelationResponseModel => { return { id: item.id, child: item.child, diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/examine-management.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/examine-management.handlers.ts index feb5d83ac5..51fca662e2 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/examine-management.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/examine-management.handlers.ts @@ -6,7 +6,7 @@ import type { IndexResponseModel, PagedIndexResponseModel, PagedSearcherResponseModel, - PagedSearchResultResponseModelReadable, + PagedSearchResultResponseModel, } from '@umbraco-cms/backoffice/external/backend-api'; export const handlers = [ @@ -65,7 +65,7 @@ export const handlers = [ if (searcherName) { return res( ctx.status(200), - ctx.json({ + ctx.json({ total: 0, items: searchResultMockData, }), diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/install.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/install.handlers.ts index 01b96ab3aa..8a259c12f4 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/install.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/install.handlers.ts @@ -1,7 +1,7 @@ const { rest } = window.MockServiceWorker; import type { DatabaseInstallRequestModel, - InstallRequestModelReadable, + InstallRequestModel, InstallSettingsResponseModel, ProblemDetails, } from '@umbraco-cms/backoffice/external/backend-api'; @@ -99,7 +99,7 @@ export const handlers = [ }), rest.post(umbracoPath('/install/setup'), async (req, res, ctx) => { - const body = await req.json(); + const body = await req.json(); if (body.database?.name === 'fail') { return res( diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/package.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/package.handlers.ts index a58cb2b86a..144a1f3848 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/package.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/package.handlers.ts @@ -6,8 +6,8 @@ import type { PagedPackageMigrationStatusResponseModel, CreatePackageRequestModel, GetPackageConfigurationResponse, - PagedPackageDefinitionResponseModelReadable, - PackageDefinitionResponseModelReadable, + PagedPackageDefinitionResponseModel, + PackageDefinitionResponseModel, } from '@umbraco-cms/backoffice/external/backend-api'; export const handlers = [ @@ -55,7 +55,7 @@ export const handlers = [ // read all return res( ctx.status(200), - ctx.json({ + ctx.json({ total: packageArray.length, items: packageArray, }), @@ -65,9 +65,9 @@ export const handlers = [ rest.post(umbracoPath('/package/created'), async (_req, res, ctx) => { //save const data: CreatePackageRequestModel = await _req.json(); - const newPackage: PackageDefinitionResponseModelReadable = { ...data, id: UmbId.new(), packagePath: '' }; + const newPackage: PackageDefinitionResponseModel = { ...data, id: UmbId.new(), packagePath: '' }; packageArray.push(newPackage); - return res(ctx.status(200), ctx.json(newPackage)); + return res(ctx.status(200), ctx.json(newPackage)); }), rest.get(umbracoPath('/package/created/:id'), (_req, res, ctx) => { @@ -76,12 +76,12 @@ export const handlers = [ if (!id) return res(ctx.status(404)); const found = packageArray.find((p) => p.id == id); if (!found) return res(ctx.status(404)); - return res(ctx.status(200), ctx.json(found)); + return res(ctx.status(200), ctx.json(found)); }), rest.put(umbracoPath('/package/created/:id'), async (_req, res, ctx) => { //update - const data: PackageDefinitionResponseModelReadable = await _req.json(); + const data: PackageDefinitionResponseModel = await _req.json(); if (!data.id) return; const index = packageArray.findIndex((x) => x.id === data.id); packageArray[index] = data; @@ -104,7 +104,7 @@ export const handlers = [ }), ]; -const packageArray: PackageDefinitionResponseModelReadable[] = [ +const packageArray: PackageDefinitionResponseModel[] = [ { id: '2a0181ec-244b-4068-a1d7-2f95ed7e6da6', packagePath: '', diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/upgrade.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/upgrade.handlers.ts index 61031c5ae7..6c733ed23f 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/upgrade.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/upgrade.handlers.ts @@ -1,14 +1,14 @@ const { rest } = window.MockServiceWorker; import { umbracoPath } from '@umbraco-cms/backoffice/utils'; -import type { UpgradeSettingsResponseModelReadable } from '@umbraco-cms/backoffice/external/backend-api'; +import type { UpgradeSettingsResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; export const handlers = [ rest.get(umbracoPath('/upgrade/settings'), (_req, res, ctx) => { return res( // Respond with a 200 status code ctx.status(200), - ctx.json({ + ctx.json({ currentState: '2b20c6e7', newState: '2b20c6e8', oldVersion: '13.0.0', 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 931d195b59..5fb10c3349 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 @@ -467,12 +467,11 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper ${this.#renderBuiltinBlockView()} + single> ${this.#renderActionBar()} ${!this._showContentEdit && this._contentInvalid ? html`!` @@ -494,7 +493,7 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper : nothing; } - #renderBuiltinBlockView() { + #renderBuiltinBlockView = () => { if (this._unsupported) { return this.#renderUnsupportedBlock(); } @@ -502,7 +501,7 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper return this.renderInlineBlock(); } return this.#renderRefBlock(); - } + }; #renderUnsupportedBlock() { return html``; } - #renderBuiltinBlockView() { + #renderBuiltinBlockView = () => { if (this._unsupported) { return this.#renderUnsupportedBlock(); } @@ -407,7 +407,7 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper return this.#renderInlineBlock(); } return this.#renderRefBlock(); - } + }; #renderBlock() { return this.contentKey && (this._contentTypeAlias || this._unsupported) @@ -417,11 +417,10 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper type="blockEditorCustomView" default-element=${this._inlineEditingMode ? 'umb-inline-list-block' : 'umb-ref-list-block'} .renderMethod=${this.#extensionSlotRenderMethod} + .fallbackRenderMethod=${this.#renderBuiltinBlockView} .props=${this._blockViewProps} .filter=${this.#extensionSlotFilterMethod} - single - >${this.#renderBuiltinBlockView()} + single> ${this.#renderActionBar()} ${!this._showContentEdit && this._contentInvalid ? html`!` 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 92129a97f2..63d79ac8f8 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 @@ -277,11 +277,10 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert type="blockEditorCustomView" default-element="umb-ref-rte-block" .renderMethod=${this.#extensionSlotRenderMethod} + .fallbackRenderMethod=${this.#renderBuiltinBlockView} .props=${this._blockViewProps} .filter=${this.#filterBlockCustomViews} - single> - ${this.#renderRefBlock()} - + single> ${this.#renderActionBar()} ${!this._showContentEdit && this._contentInvalid ? html`!` @@ -297,6 +296,14 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert : nothing; } + #renderBuiltinBlockView = () => { + // TODO: Missing unsupported rendering [NL] + /*if (this._unsupported) { + return this.#renderUnsupportedBlock(); + }*/ + return this.#renderRefBlock(); + }; + #renderRefBlock() { return html` extends UmbControllerBase @@ -62,9 +67,21 @@ export class UmbBlockElementManager, dataPathPropertyName: string) { + readonly hints; + + constructor( + host: UmbBlockWorkspaceContext, + dataPathPropertyName: string, + workspaceViewAlias: string, + ) { super(host); + this.hints = new UmbHintContext(this, { viewAlias: workspaceViewAlias }); + this.hints.inherit(); + new UmbContentValidationToHintsManager(this, this.structure, this.validation, this.hints, [ + workspaceViewAlias, + ]); + // Ugly, but we just inherit these from the workspace context: [NL] this.name = host.name; this.getName = host.getName; @@ -241,6 +258,8 @@ export class UmbBlockElementManager x?.contentKey); readonly contentKey = this.#layout.asObservablePart((x) => x?.contentKey); - readonly content = new UmbBlockElementManager(this, 'contentData'); - - readonly settings = new UmbBlockElementManager(this, 'settingsData'); + readonly content = new UmbBlockElementManager(this, 'contentData', UMB_BLOCK_WORKSPACE_VIEW_CONTENT); + readonly settings = new UmbBlockElementManager(this, 'settingsData', UMB_BLOCK_WORKSPACE_VIEW_SETTINGS); #name = new UmbStringState(undefined); readonly name = this.#name.asObservable(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/constants.ts new file mode 100644 index 0000000000..f0ecc00046 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/constants.ts @@ -0,0 +1,4 @@ +export const UMB_BLOCK_WORKSPACE_ALIAS = 'Umb.Workspace.Block'; + +export const UMB_BLOCK_WORKSPACE_VIEW_CONTENT = 'Umb.WorkspaceView.Block.Content'; +export const UMB_BLOCK_WORKSPACE_VIEW_SETTINGS = 'Umb.WorkspaceView.Block.Settings'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/index.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/index.ts index 533b373c45..4fd2a462aa 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/index.ts @@ -1,5 +1,4 @@ export * from './block-element-property-dataset.context-token.js'; export * from './block-workspace.context-token.js'; export * from './block-workspace.modal-token.js'; - -export const UMB_BLOCK_WORKSPACE_ALIAS = 'Umb.Workspace.Block'; +export * from './constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/manifests.ts index 949a4312c6..d3ac0faf73 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/manifests.ts @@ -1,4 +1,8 @@ -import { UMB_BLOCK_WORKSPACE_ALIAS } from './index.js'; +import { + UMB_BLOCK_WORKSPACE_ALIAS, + UMB_BLOCK_WORKSPACE_VIEW_CONTENT, + UMB_BLOCK_WORKSPACE_VIEW_SETTINGS, +} from './constants.js'; import { UMB_WORKSPACE_CONDITION_ALIAS, UmbSubmitWorkspaceAction } from '@umbraco-cms/backoffice/workspace'; export const manifests: Array = [ @@ -57,7 +61,7 @@ export const manifests: Array = [ }, { type: 'workspaceView', - alias: 'Umb.WorkspaceView.Block.Content', + alias: UMB_BLOCK_WORKSPACE_VIEW_CONTENT, name: 'Block Workspace Content View', element: () => import('./views/edit/block-workspace-view-edit.element.js'), weight: 1000, @@ -83,7 +87,7 @@ export const manifests: Array = [ // TODO: Fix manifest types so it support additional properties. { type: 'workspaceView', - alias: 'Umb.WorkspaceView.Block.Settings', + alias: UMB_BLOCK_WORKSPACE_VIEW_SETTINGS, name: 'Block Workspace Settings View', element: () => import('./views/edit/block-workspace-view-edit.element.js'), weight: 900, diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-content-no-router.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-content-no-router.element.ts index 06108fe2c6..c1fd3ba231 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-content-no-router.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-content-no-router.element.ts @@ -4,7 +4,7 @@ import { css, html, customElement, state, repeat, nothing } from '@umbraco-cms/b import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbContentTypeContainerStructureHelper } from '@umbraco-cms/backoffice/content-type'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { UmbPropertyTypeContainerModel } from '@umbraco-cms/backoffice/content-type'; +import type { UmbPropertyTypeContainerMergedModel } from '@umbraco-cms/backoffice/content-type'; import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/workspace'; /** @@ -20,10 +20,10 @@ export class UmbBlockWorkspaceViewEditContentNoRouterElement extends UmbLitEleme private _hasRootGroups = false; @state() - private _tabs?: Array; + private _tabs?: Array; @state() - private _activeTabId?: string | null | undefined; + private _activeTabKey?: string | null | undefined; //@state() //private _activeTabName?: string | null | undefined; @@ -36,7 +36,7 @@ export class UmbBlockWorkspaceViewEditContentNoRouterElement extends UmbLitEleme this.#tabsStructureHelper.setIsRoot(true); this.#tabsStructureHelper.setContainerChildType('Tab'); - this.observe(this.#tabsStructureHelper.mergedContainers, (tabs) => { + this.observe(this.#tabsStructureHelper.childContainers, (tabs) => { this._tabs = tabs; this.#checkDefaultTabName(); }); @@ -68,20 +68,20 @@ export class UmbBlockWorkspaceViewEditContentNoRouterElement extends UmbLitEleme if (!this._tabs || !this.#blockWorkspace) return; // Find the default tab to grab: - if (this._activeTabId === undefined) { + if (this._activeTabKey === undefined) { if (this._hasRootGroups) { //this._activeTabName = null; - this._activeTabId = null; + this._activeTabKey = null; } else if (this._tabs.length > 0) { //this._activeTabName = this._tabs[0].name; - this._activeTabId = this._tabs[0].id; + this._activeTabKey = this._tabs[0].key; } } } - #setTabName(tabName: string | undefined | null, tabId: string | null | undefined) { + #setTabName(tabName: string | undefined | null, tabKey: string | null | undefined) { //this._activeTabName = tabName; - this._activeTabId = tabId; + this._activeTabKey = tabKey; } override render() { @@ -93,7 +93,7 @@ export class UmbBlockWorkspaceViewEditContentNoRouterElement extends UmbLitEleme ? html` this.#setTabName(null, null)} >Content @@ -105,19 +105,19 @@ export class UmbBlockWorkspaceViewEditContentNoRouterElement extends UmbLitEleme (tab) => { return html` this.#setTabName(tab.name, tab.id)} + .active=${tab.key === this._activeTabKey} + @click=${() => this.#setTabName(tab.name, tab.key)} >${tab.name}`; }, )} ` : nothing} - ${this._activeTabId !== undefined + ${this._activeTabKey !== undefined ? html` + .containerId=${this._activeTabKey}> ` : nothing} `; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-tab.element.ts index 8bbd9cec4d..986598b50c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-tab.element.ts @@ -1,7 +1,7 @@ import { UMB_BLOCK_WORKSPACE_CONTEXT } from '../../block-workspace.context-token.js'; import { css, html, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UmbContentTypeModel, UmbPropertyTypeContainerModel } from '@umbraco-cms/backoffice/content-type'; +import type { UmbContentTypeModel, UmbPropertyTypeContainerMergedModel } from '@umbraco-cms/backoffice/content-type'; import { UmbContentTypeContainerStructureHelper } from '@umbraco-cms/backoffice/content-type'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; @@ -42,7 +42,7 @@ export class UmbBlockWorkspaceViewEditTabElement extends UmbLitElement { hideSingleGroup = false; @state() - private _groups: Array = []; + private _groups: Array = []; @state() private _hasProperties = false; @@ -60,7 +60,7 @@ export class UmbBlockWorkspaceViewEditTabElement extends UmbLitElement { if (!this.#blockWorkspace || !this.#managerName) return; this.#groupStructureHelper.setStructureManager(this.#blockWorkspace[this.#managerName].structure); this.observe( - this.#groupStructureHelper.mergedContainers, + this.#groupStructureHelper.childContainers, (groups) => { this._groups = groups; }, @@ -89,18 +89,18 @@ export class UmbBlockWorkspaceViewEditTabElement extends UmbLitElement { ? this.renderGroup(this._groups[0]) : repeat( this._groups, - (group) => group.id, + (group) => group.key, (group) => html` ${this.renderGroup(group)}`, )} `; } - renderGroup(group: UmbPropertyTypeContainerModel) { + renderGroup(group: UmbPropertyTypeContainerMergedModel) { return html` + .containerId=${group.ids[0]}> `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts index de633b326b..cd0ba81f9f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts @@ -3,7 +3,7 @@ import { UMB_BLOCK_WORKSPACE_CONTEXT } from '../../block-workspace.context-token import type { UmbBlockWorkspaceViewEditTabElement } from './block-workspace-view-edit-tab.element.js'; import { css, html, customElement, state, repeat, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UmbContentTypeModel, UmbPropertyTypeContainerModel } from '@umbraco-cms/backoffice/content-type'; +import type { UmbContentTypeModel, UmbPropertyTypeContainerMergedModel } from '@umbraco-cms/backoffice/content-type'; import { UmbContentTypeContainerStructureHelper } from '@umbraco-cms/backoffice/content-type'; import type { UmbRoute, UmbRouterSlotChangeEvent, UmbRouterSlotInitEvent } from '@umbraco-cms/backoffice/router'; import { encodeFolderName } from '@umbraco-cms/backoffice/router'; @@ -34,7 +34,7 @@ export class UmbBlockWorkspaceViewEditElement extends UmbLitElement implements U private _routes: UmbRoute[] = []; @state() - private _tabs?: Array; + private _tabs?: Array; @state() private _routerPath?: string; @@ -48,7 +48,7 @@ export class UmbBlockWorkspaceViewEditElement extends UmbLitElement implements U this.#tabsStructureHelper.setIsRoot(true); this.#tabsStructureHelper.setContainerChildType('Tab'); this.observe( - this.#tabsStructureHelper.mergedContainers, + this.#tabsStructureHelper.childContainers, (tabs) => { this._tabs = tabs; this.#createRoutes(); @@ -102,7 +102,7 @@ export class UmbBlockWorkspaceViewEditElement extends UmbLitElement implements U component: () => import('./block-workspace-view-edit-tab.element.js'), setup: (component) => { (component as UmbBlockWorkspaceViewEditTabElement).managerName = this.#managerName; - (component as UmbBlockWorkspaceViewEditTabElement).containerId = tab.id; + (component as UmbBlockWorkspaceViewEditTabElement).containerId = tab.ids[0]; }, }); }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-container-structure-helper.class.ts index 58afbe2cb0..9558578e39 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-container-structure-helper.class.ts @@ -1,8 +1,13 @@ -import type { UmbContentTypeModel, UmbPropertyContainerTypes, UmbPropertyTypeContainerModel } from '../types.js'; +import type { + UmbContentTypeModel, + UmbPropertyContainerTypes, + UmbPropertyTypeContainerMergedModel, + UmbPropertyTypeContainerModel, +} from '../types.js'; import type { UmbContentTypeStructureManager } from './content-type-structure-manager.class.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbController, UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; +import { UmbArrayState, UmbBooleanState } from '@umbraco-cms/backoffice/observable-api'; /** * This class is a helper class for managing the structure of containers in a content type. @@ -11,6 +16,7 @@ import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; export class UmbContentTypeContainerStructureHelper extends UmbControllerBase { #init; #initResolver?: (value: unknown) => void; + #initRejector?: () => void; #containerId?: string | null; #childType?: UmbPropertyContainerTypes = 'Group'; @@ -21,31 +27,55 @@ export class UmbContentTypeContainerStructureHelper([], (x) => x.id); - readonly containers = this.#childContainers.asObservable(); + get containers() { + this.#startLegacy(); + return this.#childContainers.asObservable(); + } // State containing the merged containers (only one pr. name): - #mergedChildContainers = new UmbArrayState([], (x) => x.id); - readonly mergedContainers = this.#mergedChildContainers.asObservable(); + #legacyMergedChildContainers = new UmbArrayState([], (x) => x.id); + get mergedContainers() { + this.#startLegacy(); + return this.#legacyMergedChildContainers.asObservable(); + } + + #childContainersMerged = new UmbArrayState([], (x) => x.path); + public readonly childContainers = this.#childContainersMerged.asObservable(); // Owner containers are containers owned by the owner Content Type (The specific one up for editing) #ownerChildContainers: UmbPropertyTypeContainerModel[] = []; - #hasProperties = new UmbArrayState<{ id: string | null; has: boolean }>([], (x) => x.id); - readonly hasProperties = this.#hasProperties.asObservablePart((x) => x.some((y) => y.has)); + #hasProperties = new UmbBooleanState(false); + readonly hasProperties = this.#hasProperties.asObservable(); constructor(host: UmbControllerHost) { super(host); - this.#init = new Promise((resolve) => { + this.#init = new Promise((resolve, reject) => { this.#initResolver = resolve; + this.#initRejector = reject; }); - this.#mergedChildContainers.sortBy((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0)); - this.observe(this.containers, this.#performContainerMerge, null); + this.#legacyMergedChildContainers.sortBy((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0)); + } + + // TODO: Implement UmbDeprecated and Obsolete this from v.17 [NL] + #legacyMergeLogic = false; + #startLegacy() { + if (this.#legacyMergeLogic) return; + console.log( + "Pst. we will be deprecating 'mergedContainers' and 'containers' in v.17.0, feel free to use them until v.18.0. But please use 'childContainers'", + ); + this.#legacyMergeLogic = true; + this.#legacyObserveContainers(); + this.observe(this.containers, this.#legacyPerformContainerMerge, null); } public setStructureManager(structure: UmbContentTypeStructureManager | undefined) { if (this.#structure === structure || !structure) return; if (this.#structure && !structure) { + this.#initRejector?.(); + this.#initResolver = undefined; + this.#initRejector = undefined; throw new Error( 'Structure manager is already set, the helpers are not designed to be re-setup with new managers', ); @@ -53,7 +83,9 @@ export class UmbContentTypeContainerStructureHelper { + this.#childContainersMerged.setValue(childContainers ?? []); + }, + 'observeChildContainers', + ); + + if (this.#containerId === null) { + this.removeUmbControllerByAlias('observeParentContainer'); + // Observe root properties: + this.observe( + this.#structure?.hasPropertyStructuresOfRoot(), + (has) => { + this.#hasProperties.setValue(has ?? false); + }, + 'observeProperties', + ); + } else { + // Observe properties of the parent container and matching containers (therefor getting the merged container of the parent id): [NL] + const parentObservable = + this.#containerId !== undefined && this.#childType + ? this.#structure?.mergedContainersOfId(this.#containerId) + : undefined; + + this.observe( + parentObservable, + (parentContainer) => { + this.observe( + parentContainer ? this.#structure?.hasPropertyStructuresOfGroupIds(parentContainer.ids ?? []) : undefined, + (has) => { + this.#hasProperties.setValue(has ?? false); + }, + 'observeProperties', + ); + }, + 'observeParentContainer', + ); + } + } + + // LEGACY properties: #containerName?: string; #containerType?: UmbPropertyContainerTypes; #parentName?: string | null; #parentType?: UmbPropertyContainerTypes; - #observeContainers() { + // LEGACY method: + #legacyObserveContainers() { + if (!this.#legacyMergeLogic) return; if (!this.#structure || this.#containerId === undefined) return; if (this.#containerId === null) { - this.#observeHasPropertiesOf(null); - this.#observeRootContainers(); + //this.#observeHasPropertiesOf(null); + this.#legacyObserveRootContainers(); this.removeUmbControllerByAlias('_observeContainers'); } else { this.observe( @@ -133,8 +218,7 @@ export class UmbContentTypeContainerStructureHelper { - this.#hasProperties.setValue([]); + //this.#hasProperties.setValue([]); this.#childContainers.setValue([]); this.#containerObservers.forEach((x) => x.destroy()); this.#containerObservers = []; containers.forEach((container) => { - this.#observeHasPropertiesOf(container.id); + //this.#observeHasPropertiesOf(container.id); this.#containerObservers.push( this.observe( @@ -184,7 +269,8 @@ export class UmbContentTypeContainerStructureHelper) { + // LEGACY method: + #legacyFilterNonOwnerContainers(containers: Array) { return this.#ownerChildContainers.length > 0 ? containers.filter( (anyCon) => @@ -226,22 +315,23 @@ export class UmbContentTypeContainerStructureHelper) => { + // LEGACY method: + #legacyPerformContainerMerge = (containers: Array) => { // Remove containers that matches with a owner container: - let merged = this.#filterNonOwnerContainers(containers); + let merged = this.#legacyFilterNonOwnerContainers(containers); // Remove containers of same name and type: // This only works cause we are dealing with a single level of containers in this Helper, if we had more levels we would need to be more clever about the parent as well. [NL] merged = merged.filter((x, i, cons) => i === cons.findIndex((y) => y.name === x.name && y.type === x.type)); - this.#mergedChildContainers.setValue(merged); + this.#legacyMergedChildContainers.setValue(merged); }; /** * Returns true if the container is an owner container. * @param containerId */ - isOwnerChildContainer(containerId?: string) { + isOwnerChildContainer(containerId?: string): boolean | undefined { if (!this.#structure || !containerId) return; - return this.#ownerChildContainers.some((x) => x.id === containerId); + return this.#structure.isOwnerContainer(containerId); } getContentTypeOfContainer(containerId?: string) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-property-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-property-structure-helper.class.ts index 10237041bf..be18514559 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-property-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-property-structure-helper.class.ts @@ -47,7 +47,7 @@ export class UmbContentTypePropertyStructureHelper { diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-structure-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-structure-manager.class.ts index 192a36204a..6d97aaef57 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-structure-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-structure-manager.class.ts @@ -497,19 +497,38 @@ export class UmbContentTypeStructureManager< makeEmptyContainerName( containerId: string, - containerType: UmbPropertyContainerTypes, - parentId: string | null = null, + legacyContainerType?: UmbPropertyContainerTypes, + legacyParentId?: string | null, ): string { return ( - this.makeContainerNameUniqueForOwnerContentType(containerId, 'Unnamed', containerType, parentId) ?? 'Unnamed' + this.makeContainerNameUniqueForOwnerContentType(containerId, 'Unnamed', legacyContainerType, legacyParentId) ?? + 'Unnamed' ); } + /** + * + * @param {string} containerId - The id of the container to make unique + * @param {string} newName - The new name to make unique + * @param {never} _legacyContainerType - do not use, has no effect. Is deprecated and will be removed in v.17 + * @param {never} _legacyParentId - do not use, has no effect. Is deprecated and will be removed in v.17 + * @returns + */ makeContainerNameUniqueForOwnerContentType( containerId: string, newName: string, - containerType: UmbPropertyContainerTypes, - parentId: string | null = null, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _legacyContainerType?: UmbPropertyContainerTypes, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _legacyParentId?: string | null, ) { + const container = this.getOwnerContainerById(containerId); + if (!container) { + console.warn(`Container with id ${containerId} not found in owner content type.`); + return null; + } + const containerType = container.type; + const parentId = container.parent?.id ?? null; + const ownerRootContainers = this.getOwnerContainers(containerType, parentId); //getRootContainers() can't differentiates between compositions and locals if (!ownerRootContainers) { return null; @@ -749,6 +768,26 @@ export class UmbContentTypeStructureManager< }); } + hasPropertyStructuresOfGroupIds(groupIds: Array) { + return this.#contentTypes.asObservablePart((docTypes) => { + return docTypes.some((docType) => { + return docType.properties?.some((property) => { + return property.container?.id && groupIds.includes(property.container.id); + }); + }); + }); + } + + hasPropertyStructuresOfRoot() { + return this.#contentTypes.asObservablePart((docTypes) => { + return docTypes.some((docType) => { + return docType.properties?.some((property) => { + return !property.container; + }); + }); + }); + } + rootContainers(containerType: UmbPropertyContainerTypes) { return createObservablePart(this.#contentTypeContainers, (data) => { return data.filter((x) => x.parent === null && x.type === containerType); @@ -953,6 +992,7 @@ export class UmbContentTypeStructureManager< * Find merged containers that match the provided container ids. * Notice if you can provide one or more ids matching the same container and it will still only return return the matching container once. * @param containerIds - An array of container ids to find merged containers for. + * @param id * @returns {UmbPropertyTypeContainerMergedModel | undefined} - The merged containers that match the provided container ids. */ getMergedContainerById(id: string): UmbPropertyTypeContainerMergedModel | undefined { @@ -964,6 +1004,7 @@ export class UmbContentTypeStructureManager< * Find merged child containers that are children of the provided parent container ids. * Notice this will find matching containers and include their child containers in this. * @param containerIds - An array of container ids to find merged child containers for. + * @param searchId * @param type - The type of the containers to find. * @returns {Observable} - An observable that emits the merged child containers that match the provided container ids. */ diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor-group.element.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor-group.element.ts index 2745074f62..68f83dac68 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor-group.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor-group.element.ts @@ -1,4 +1,4 @@ -import type { UmbContentTypeModel, UmbPropertyTypeContainerModel } from '../../../types.js'; +import type { UmbContentTypeModel, UmbPropertyTypeContainerMergedModel } from '../../../types.js'; import type { UmbContentTypeContainerStructureHelper } from '../../../structure/index.js'; import { css, customElement, html, nothing, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; import { umbConfirmModal } from '@umbraco-cms/backoffice/modal'; @@ -11,16 +11,16 @@ import './content-type-design-editor-properties.element.js'; @customElement('umb-content-type-design-editor-group') export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { @property({ attribute: false }) - public set group(value: UmbPropertyTypeContainerModel | undefined) { + public set group(value: UmbPropertyTypeContainerMergedModel | undefined) { if (value === this._group) return; this._group = value; - this._groupId = value?.id; + this._groupId = value?.ownerId ?? value?.ids[0]; this.#checkInherited(); } - public get group(): UmbPropertyTypeContainerModel | undefined { + public get group(): UmbPropertyTypeContainerMergedModel | undefined { return this._group; } - private _group?: UmbPropertyTypeContainerModel | undefined; + private _group?: UmbPropertyTypeContainerMergedModel | undefined; @property({ attribute: false }) public set groupStructureHelper(value: UmbContentTypeContainerStructureHelper | undefined) { @@ -45,7 +45,8 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { @state() private _hasOwnerContainer?: boolean; - @state() + // attrbute is used by Sorter Controller in parent scope. + @property({ type: Boolean, reflect: true, attribute: 'inherited' }) private _inherited?: boolean; @state() @@ -54,48 +55,46 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { #checkInherited() { if (this.groupStructureHelper && this.group) { // Check is this container matches with any other group. If so it is inherited aka. merged with others. [NL] - if (this.group.name) { - // We can first match with something if we have a name [NL] - this.observe( - this.groupStructureHelper.containersByNameAndType(this.group.name, 'Group'), - (containers) => { - const ownerContainer = containers.find((con) => this.groupStructureHelper!.isOwnerChildContainer(con.id)); - const hasAOwnerContainer = !!ownerContainer; - const pureOwnerContainer = hasAOwnerContainer && containers.length === 1; - - this._hasOwnerContainer = hasAOwnerContainer; - this._inherited = !pureOwnerContainer; - this._inheritedFrom = containers - .filter((con) => con.id !== ownerContainer?.id) - .map((con) => this.groupStructureHelper!.getContentTypeOfContainer(con.id)) - .filter((contentType) => contentType !== undefined) as Array; - }, - 'observeGroupContainers', - ); - } else { - // We use name match to determine inheritance, so no name cannot inherit. - this._inherited = false; + if (this.group.ownerId) { this._hasOwnerContainer = true; - this.removeUmbControllerByAlias('observeGroupContainers'); + } + + const notOwnerContainerIds = this.group.ids.filter((id) => id !== this.group!.ownerId); + + if (notOwnerContainerIds.length > 0) { + this._inheritedFrom = notOwnerContainerIds + .map((id) => this.groupStructureHelper!.getContentTypeOfContainer(id)) + .filter((contentType) => contentType !== undefined) as Array; + this._inherited = true; + } else { + this._inheritedFrom = undefined; + this._inherited = false; } } } #singleValueUpdate(propertyName: string, value: string | number | boolean | null | undefined) { - if (!this._groupStructureHelper || !this.group) return; + if (!this._groupStructureHelper || !this._group) return; + + const ownerId = this._group.ownerId; + if (!ownerId) return; const partialObject = {} as any; partialObject[propertyName] = value; - this._groupStructureHelper.partialUpdateContainer(this.group.id, partialObject); + this._groupStructureHelper.partialUpdateContainer(ownerId, partialObject); } #renameGroup(e: InputEvent) { if (!this.groupStructureHelper || !this._group) return; + const ownerId = this._group.ownerId; + if (!ownerId) return; let newName = (e.target as HTMLInputElement).value; + // TODO: This does not seem right, the detection of a unique name requires better awareness on the level of the change. [NL] + // This seem to use check for root containers. const changedName = this.groupStructureHelper .getStructureManager()! - .makeContainerNameUniqueForOwnerContentType(this._group.id, newName, 'Group', this._group.parent?.id ?? null); + .makeContainerNameUniqueForOwnerContentType(ownerId, newName); if (changedName) { newName = changedName; } @@ -105,11 +104,11 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { #blurGroup(e: InputEvent) { if (!this.groupStructureHelper || !this._group) return; + const ownerId = this._group.ownerId; + if (!ownerId) return; const newName = (e.target as HTMLInputElement).value; if (newName === '') { - const changedName = this.groupStructureHelper - .getStructureManager()! - .makeEmptyContainerName(this._group.id, 'Group', this._group.parent?.id ?? null); + const changedName = this.groupStructureHelper.getStructureManager()!.makeEmptyContainerName(ownerId); this.#singleValueUpdate('name', changedName); (e.target as HTMLInputElement).value = changedName; } @@ -119,21 +118,22 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { e.preventDefault(); e.stopImmediatePropagation(); if (!this.groupStructureHelper || !this._group) return; + if (this._group.ownerId === undefined) return; // TODO: Do proper localization here: [NL] await umbConfirmModal(this, { headline: `${this.localize.term('actions_delete')} group`, content: html` - Are you sure you want to delete the group ${this._group.name ?? this._group.id} + Are you sure you want to delete the group ${this._group.name ?? this._group.ownerId} `, confirmLabel: this.localize.term('actions_delete'), color: 'danger', }); - this.groupStructureHelper.removeContainer(this._group.id); + this.groupStructureHelper.removeContainer(this._group.ownerId); } override render() { @@ -277,6 +277,10 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { padding: var(--uui-size-space-4) var(--uui-size-space-5); } + :host([inherited]) div[slot='header'] { + cursor: default; + } + div[slot='header'] > div { display: flex; align-items: center; diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor-property.element.ts index 49adf60673..75b3f5b8c8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor-property.element.ts @@ -456,6 +456,10 @@ export class UmbContentTypeDesignEditorPropertyElement extends UmbLitElement { height: min-content; } + #header i { + opacity: 0.55; + } + #editor { position: relative; --uui-button-background-color: var(--uui-color-background); diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor-tab.element.ts index 051f6caffc..34e8ddf56c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor-tab.element.ts @@ -1,5 +1,9 @@ import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '../../content-type-workspace.context-token.js'; -import type { UmbContentTypeModel, UmbPropertyTypeContainerModel } from '../../../types.js'; +import type { + UmbContentTypeModel, + UmbPropertyTypeContainerMergedModel, + UmbPropertyTypeContainerModel, +} from '../../../types.js'; import { UmbContentTypeContainerStructureHelper } from '../../../structure/index.js'; import { UMB_CONTENT_TYPE_DESIGN_EDITOR_CONTEXT } from './content-type-design-editor.context-token.js'; import type { UmbContentTypeWorkspaceViewEditGroupElement } from './content-type-design-editor-group.element.js'; @@ -13,95 +17,122 @@ import type { UmbSorterConfig } from '@umbraco-cms/backoffice/sorter'; import './content-type-design-editor-properties.element.js'; import './content-type-design-editor-group.element.js'; -const SORTER_CONFIG: UmbSorterConfig = { - getUniqueOfElement: (element) => element.group?.id, - getUniqueOfModel: (modelEntry) => modelEntry.id, - // TODO: Make specific to the current owner document. [NL] - identifier: 'content-type-container-sorter', - itemSelector: 'umb-content-type-design-editor-group', - handleSelector: '.drag-handle', - containerSelector: '.container-list', -}; +const SORTER_CONFIG: UmbSorterConfig = + { + getUniqueOfElement: (element) => element.group?.key, + getUniqueOfModel: (modelEntry) => modelEntry.key, + // TODO: Make specific to the current owner document. [NL] + identifier: 'content-type-container-sorter', + itemSelector: 'umb-content-type-design-editor-group', + handleSelector: '.drag-handle', + disabledItemSelector: '[inherited]', // Inherited attribute is set by the umb-content-type-design-editor-group. + containerSelector: '.container-list', + }; @customElement('umb-content-type-design-editor-tab') export class UmbContentTypeDesignEditorTabElement extends UmbLitElement { - #sorter = new UmbSorterController(this, { - ...SORTER_CONFIG, - onChange: ({ model }) => { - this._groups = model; - }, - onEnd: ({ item }) => { - /*if (this._inherited === undefined) { + #sorter = new UmbSorterController( + this, + { + ...SORTER_CONFIG, + onChange: ({ model }) => { + this._groups = model; + }, + onEnd: ({ item }) => { + /*if (this._inherited === undefined) { throw new Error('OwnerTabId is not set, we have not made a local duplicated of this container.'); return; }*/ - /** - * Explanation: If the item is the first in list, we compare it to the item behind it to set a sortOrder. - * If it's not the first in list, we will compare to the item in before it, and check the following item to see if it caused overlapping sortOrder, then update - * the overlap if true, which may cause another overlap, so we loop through them till no more overlaps... - */ - const model = this._groups; - const newIndex = model.findIndex((entry) => entry.id === item.id); + /** + * Explanation: If the item is the first in list, we compare it to the item behind it to set a sortOrder. + * If it's not the first in list, we will compare to the item in before it, and check the following item to see if it caused overlapping sortOrder, then update + * the overlap if true, which may cause another overlap, so we loop through them till no more overlaps... + */ + const model = this._groups; + const newIndex = model.findIndex((entry) => entry.key === item.key); - // Doesn't exist in model - if (newIndex === -1) return; + // Doesn't exist in model + if (newIndex === -1) return; - // As origin we set prev sort order to -1, so if no other then our item will become 0 - let prevSortOrder = -1; + // As origin we set prev sort order to -1, so if no other then our item will become 0 + let prevSortOrder = -1; - // Not first in list - if (newIndex > 0 && model.length > 0) { - prevSortOrder = model[newIndex - 1].sortOrder; - } + // Not first in list + if (newIndex > 0 && model.length > 0) { + prevSortOrder = model[newIndex - 1].sortOrder; + } - // increase the prevSortOrder and use it for the moved item, - this.#groupStructureHelper.partialUpdateContainer(item.id, { - sortOrder: ++prevSortOrder, - }); + const ownerId = item.ownerId; - // Adjust everyone right after, meaning until there is a gap between the sortOrders: - let i = newIndex + 1; - let entry: UmbPropertyTypeContainerModel | undefined; - // As long as there is an item with the index & the sortOrder is less or equal to the prevSortOrder, we will update the sortOrder: - while ((entry = model[i]) !== undefined && entry.sortOrder <= prevSortOrder) { - // Increase the prevSortOrder and use it for the item: - this.#groupStructureHelper.partialUpdateContainer(entry.id, { + if (ownerId === undefined) { + // This may be possible later, but for now this is not possible. [NL] + throw new Error( + 'OwnerId is not set for the given container, we cannot move containers that are not owned by the current Document.', + ); + } + + // increase the prevSortOrder and use it for the moved item, + this.#groupStructureHelper.partialUpdateContainer(ownerId, { sortOrder: ++prevSortOrder, }); - i++; - } + // Adjust everyone right after, meaning until there is a gap between the sortOrders: + let i = newIndex + 1; + let entry: UmbPropertyTypeContainerMergedModel | undefined; + // As long as there is an item with the index & the sortOrder is less or equal to the prevSortOrder, we will update the sortOrder: + while ((entry = model[i]) !== undefined && entry.sortOrder <= prevSortOrder) { + // Only updated owned containers: + if (entry.ownerId) { + // Increase the prevSortOrder and use it for the item: + this.#groupStructureHelper.partialUpdateContainer(entry.ownerId, { + sortOrder: ++prevSortOrder, + }); + + i++; + } + } + }, + onRequestDrop: async ({ unique }) => { + const context = await this.getContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT); + if (!context) { + throw new Error('Could not get Workspace Context'); + } + return context.structure.getMergedContainerById(unique) as UmbPropertyTypeContainerMergedModel | undefined; + }, + requestExternalRemove: async ({ item }) => { + const context = await this.getContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT); + if (!context) { + throw new Error('Could not get Workspace Context'); + } + return await context.structure.removeContainer(null, item.ownerId, { preventRemovingProperties: true }).then( + () => true, + () => false, + ); + }, + requestExternalInsert: async ({ item }) => { + const context = await this.getContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT); + if (!context) { + throw new Error('Could not get Workspace Context'); + } + if (item.ownerId === undefined) { + // This may be possible later, but for now this is not possible. [NL] + throw new Error('OwnerId is not set, we cannot move containers that are not owned by the current Document.'); + } + const parent = this.#containerId ? { id: this.#containerId } : null; + const containerModelItem: UmbPropertyTypeContainerModel = { + id: item.ownerId, + name: item.name, + sortOrder: item.sortOrder, + type: item.type, + parent, + }; + return await context.structure.insertContainer(null, containerModelItem).then( + () => true, + () => false, + ); + }, }, - onRequestDrop: async ({ unique }) => { - const context = await this.getContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT); - if (!context) { - throw new Error('Could not get Workspace Context'); - } - return context.structure.getOwnerContainerById(unique); - }, - requestExternalRemove: async ({ item }) => { - const context = await this.getContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT); - if (!context) { - throw new Error('Could not get Workspace Context'); - } - return await context.structure.removeContainer(null, item.id, { preventRemovingProperties: true }).then( - () => true, - () => false, - ); - }, - requestExternalInsert: async ({ item }) => { - const context = await this.getContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT); - if (!context) { - throw new Error('Could not get Workspace Context'); - } - const parent = this.#containerId ? { id: this.#containerId } : null; - const updatedItem = { ...item, parent }; - return await context.structure.insertContainer(null, updatedItem).then( - () => true, - () => false, - ); - }, - }); + ); #workspaceModal?: UmbModalRouteRegistrationController< typeof UMB_WORKSPACE_MODAL.DATA, @@ -122,7 +153,7 @@ export class UmbContentTypeDesignEditorTabElement extends UmbLitElement { } @state() - private _groups: Array = []; + private _groups: Array = []; @state() private _hasProperties = false; @@ -170,7 +201,7 @@ export class UmbContentTypeDesignEditorTabElement extends UmbLitElement { }); this.observe( - this.#groupStructureHelper.mergedContainers, + this.#groupStructureHelper.childContainers, (groups) => { this._groups = groups; this.#sorter.setModel(this._groups); @@ -212,14 +243,14 @@ export class UmbContentTypeDesignEditorTabElement extends UmbLitElement {
${repeat( this._groups, - (group) => group.id, + (group) => group.ids[0], (group) => html` `, diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor.element.ts index d37eef19a0..9f632ab6fc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content-type/workspace/views/design/content-type-design-editor.element.ts @@ -2,7 +2,7 @@ import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '../../content-type-workspace import type { UmbContentTypeCompositionModel, UmbContentTypeModel, - UmbPropertyTypeContainerModel, + UmbPropertyTypeContainerMergedModel, } from '../../../types.js'; import { UmbContentTypeContainerStructureHelper, @@ -33,9 +33,9 @@ import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; @customElement('umb-content-type-design-editor') export class UmbContentTypeDesignEditorElement extends UmbLitElement implements UmbWorkspaceViewElement { - #sorter = new UmbSorterController(this, { - getUniqueOfElement: (element) => element.getAttribute('data-umb-tab-id'), - getUniqueOfModel: (tab) => tab.id, + #sorter = new UmbSorterController(this, { + getUniqueOfElement: (element) => element.getAttribute('data-umb-tab-key'), + getUniqueOfModel: (tab) => tab.key, identifier: 'content-type-tabs-sorter', itemSelector: 'uui-tab', containerSelector: 'uui-tab-group', @@ -51,7 +51,7 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements * the overlap if true, which may cause another overlap, so we loop through them till no more overlaps... */ const model = this._tabs ?? []; - const newIndex = model.findIndex((entry) => entry.id === item.id); + const newIndex = model.findIndex((entry) => entry.key === item.key); // Doesn't exist in model if (newIndex === -1) return; @@ -64,20 +64,32 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements prevSortOrder = model[newIndex - 1].sortOrder; } + const ownerId = item.ownerId; + + if (ownerId === undefined) { + // This may be possible later, but for now this is not possible. [NL] + throw new Error( + 'OwnerId is not set for the given container, we cannot move containers that are not owned by the current Document.', + ); + } + // increase the prevSortOrder and use it for the moved item, - this.#tabsStructureHelper.partialUpdateContainer(item.id, { + this.#tabsStructureHelper.partialUpdateContainer(ownerId, { sortOrder: ++prevSortOrder, }); // Adjust everyone right after, until there is a gap between the sortOrders: [NL] let i = newIndex + 1; - let entry: UmbPropertyTypeContainerModel | undefined; + let entry: UmbPropertyTypeContainerMergedModel | undefined; // As long as there is an item with the index & the sortOrder is less or equal to the prevSortOrder, we will update the sortOrder: while ((entry = model[i]) !== undefined && entry.sortOrder <= prevSortOrder) { - // Increase the prevSortOrder and use it for the item: - this.#tabsStructureHelper.partialUpdateContainer(entry.id, { - sortOrder: ++prevSortOrder, - }); + // Only updated owned containers: + if (entry.ownerId) { + // Increase the prevSortOrder and use it for the item: + this.#tabsStructureHelper.partialUpdateContainer(entry.ownerId, { + sortOrder: ++prevSortOrder, + }); + } i++; } @@ -105,7 +117,7 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements private _routes: UmbRoute[] = []; @state() - private _tabs?: Array; + private _tabs?: Array; @state() private _routerPath?: string; @@ -136,7 +148,7 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements this.#tabsStructureHelper.setContainerChildType('Tab'); this.#tabsStructureHelper.setIsRoot(true); - this.observe(this.#tabsStructureHelper.mergedContainers, (tabs) => { + this.observe(this.#tabsStructureHelper.childContainers, (tabs) => { this._tabs = tabs; this.#sorter.setModel(tabs); this.#createRoutes(); @@ -180,7 +192,7 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements if (this._tabs.length > 0) { this._tabs?.forEach((tab) => { const tabName = tab.name && tab.name !== '' ? tab.name : '-'; - if (tab.id === this.#processingTabId) { + if (tab.ownerId && tab.ownerId === this.#processingTabId) { activeTabName = tabName; } routes.push({ @@ -188,7 +200,7 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements component: () => import('./content-type-design-editor-tab.element.js'), setup: (component) => { this.#currentTabComponent = component as UmbContentTypeDesignEditorTabElement; - this.#currentTabComponent.containerId = tab.id; + this.#currentTabComponent.containerId = tab.ownerId ?? tab.ids[0]; }, }); }); @@ -265,8 +277,8 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements } } - async #requestDeleteTab(tab: UmbPropertyTypeContainerModel | undefined) { - if (!tab) return; + async #requestDeleteTab(tab: UmbPropertyTypeContainerMergedModel | undefined) { + if (!tab || !tab.ownerId) return; // TODO: Localize this: const tabName = tab.name === '' ? 'Unnamed' : tab.name; // TODO: Localize this: @@ -288,10 +300,9 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements await umbConfirmModal(this, modalData); - this.#deleteTab(tab?.id); + this.#deleteTab(tab.ownerId); } - #deleteTab(tabId?: string) { - if (!tabId) return; + #deleteTab(tabId: string) { this.#workspaceContext?.structure.removeContainer(null, tabId); if (this.#processingTabId === tabId) { this.#processingTabId = undefined; @@ -332,12 +343,13 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements }, 100); } - async #tabNameChanged(event: InputEvent, tab: UmbPropertyTypeContainerModel) { - this.#processingTabId = tab.id; + async #tabNameChanged(event: InputEvent, tab: UmbPropertyTypeContainerMergedModel) { + if (!this.#workspaceContext || !tab.ownerId) return; + this.#processingTabId = tab.ownerId; let newName = (event.target as HTMLInputElement).value; - const changedName = this.#workspaceContext?.structure.makeContainerNameUniqueForOwnerContentType( - tab.id, + const changedName = this.#workspaceContext.structure.makeContainerNameUniqueForOwnerContentType( + tab.ownerId, newName, 'Tab', ); @@ -349,20 +361,20 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements (event.target as HTMLInputElement).value = newName; } - this.#tabsStructureHelper.partialUpdateContainer(tab.id!, { + this.#tabsStructureHelper.partialUpdateContainer(tab.ownerId, { name: newName, }); } - async #tabNameBlur(event: FocusEvent, tab: UmbPropertyTypeContainerModel) { - if (!this.#processingTabId) return; + async #tabNameBlur(event: FocusEvent, tab: UmbPropertyTypeContainerMergedModel) { + if (!this.#processingTabId || !tab.ownerId) return; const newName = (event.target as HTMLInputElement | undefined)?.value; if (newName === '') { const changedName = this.#workspaceContext!.structure.makeEmptyContainerName(this.#processingTabId, 'Tab'); (event.target as HTMLInputElement).value = changedName; - this.#tabsStructureHelper.partialUpdateContainer(tab.id!, { + this.#tabsStructureHelper.partialUpdateContainer(tab.ownerId, { name: changedName, }); } @@ -506,7 +518,7 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements ${this.renderRootTab()} ${repeat( this._tabs, - (tab) => tab.id, + (tab) => tab.ownerId ?? tab.ids[0], (tab) => this.renderTab(tab), )} @@ -535,16 +547,17 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements `; } - renderTab(tab: UmbPropertyTypeContainerModel) { + renderTab(tab: UmbPropertyTypeContainerMergedModel) { const path = this._routerPath + '/tab/' + encodeFolderName(tab.name && tab.name !== '' ? tab.name : '-'); const tabActive = path === this._activePath; - const ownedTab = this.#tabsStructureHelper.isOwnerChildContainer(tab.id!) ?? false; + const ownedTab = tab.ownerId ? true : false; return html` this.#onDragOver(event, path)}> @@ -552,14 +565,15 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements `; } - renderTabInner(tab: UmbPropertyTypeContainerModel, tabActive: boolean, ownedTab: boolean) { + renderTabInner(tab: UmbPropertyTypeContainerMergedModel, tabActive: boolean, ownedTab: boolean) { // TODO: Localize this: const hasTabName = tab.name && tab.name !== ''; const tabName = hasTabName ? tab.name : 'Unnamed'; + const tabId = tab.ownerId ?? tab.ids[0]; if (this._sortModeActive) { return html`
${ownedTab - ? html` ${tabName} + ? html` ${tabName} , saveData: DetailModelType) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/content-validation-to-hints.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/content-validation-to-hints.manager.ts index 8dcd90539b..4dad30c592 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/content-validation-to-hints.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/content-validation-to-hints.manager.ts @@ -6,7 +6,7 @@ import type { } from '@umbraco-cms/backoffice/content-type'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbHintController, UmbVariantHint } from '@umbraco-cms/backoffice/hint'; -import { extractJsonQueryProps, type UmbValidationContext } from '@umbraco-cms/backoffice/validation'; +import { extractJsonQueryProps, type UmbValidationController } from '@umbraco-cms/backoffice/validation'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; /* @@ -21,7 +21,7 @@ export class UmbContentValidationToHintsManager< > extends UmbControllerBase { /*workspace.hints.addOne({ unique: 'exampleHintFromToggleAction', - path: ['Umb.WorkspaceView.Document.Edit', 'root'], + path: ['Umb.WorkspaceView.Document.Edit'], text: 'Hi', color: 'invalid', weight: 100, @@ -39,8 +39,9 @@ export class UmbContentValidationToHintsManager< constructor( host: UmbControllerHost, structure: UmbContentTypeStructureManager, - validation: UmbValidationContext, + validation: UmbValidationController, hints: UmbHintController, + hintsPathPrefix: Array = ['Umb.WorkspaceView.Document.Edit'], ) { super(host); @@ -77,7 +78,7 @@ export class UmbContentValidationToHintsManager< hints.addOne({ unique: message.key, - path: ['Umb.WorkspaceView.Document.Edit', ...path], + path: [...hintsPathPrefix, ...path], text: '!', /*label: message.body,*/ color: 'invalid', diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/index.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/index.ts index 40d5ffc230..8428753305 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/index.ts @@ -1,2 +1,3 @@ export * from './content-detail-workspace-base.js'; +export * from './content-validation-to-hints.manager.js'; export type * from './content-workspace-context.interface.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/views/edit/content-editor-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/views/edit/content-editor-tab.element.ts index fffc7743a0..1366433e5e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/views/edit/content-editor-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/views/edit/content-editor-tab.element.ts @@ -3,7 +3,7 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbContentTypeModel, UmbContentTypeStructureManager, - UmbPropertyTypeContainerModel, + UmbPropertyTypeContainerMergedModel, } from '@umbraco-cms/backoffice/content-type'; import { UmbContentTypeContainerStructureHelper, @@ -28,7 +28,7 @@ export class UmbContentWorkspaceViewEditTabElement extends UmbLitElement { #groupStructureHelper = new UmbContentTypeContainerStructureHelper(this); @state() - private _groups: Array = []; + private _groups: Array = []; @state() private _hasProperties = false; @@ -42,7 +42,7 @@ export class UmbContentWorkspaceViewEditTabElement extends UmbLitElement { workspaceContext?.structure as unknown as UmbContentTypeStructureManager, ); }); - this.observe(this.#groupStructureHelper.mergedContainers, (groups) => { + this.observe(this.#groupStructureHelper.childContainers, (groups) => { this._groups = groups; }); this.observe(this.#groupStructureHelper.hasProperties, (hasProperties) => { @@ -63,12 +63,12 @@ export class UmbContentWorkspaceViewEditTabElement extends UmbLitElement { : ''} ${repeat( this._groups, - (group) => group.id, + (group) => group.key, (group) => html` + .containerId=${group.ids[0]}> `, )} `; diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/views/edit/content-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/views/edit/content-editor.element.ts index dce11b6601..f5b9941d44 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/views/edit/content-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/views/edit/content-editor.element.ts @@ -4,7 +4,7 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbContentTypeModel, UmbContentTypeStructureManager, - UmbPropertyTypeContainerModel, + UmbPropertyTypeContainerMergedModel, } from '@umbraco-cms/backoffice/content-type'; import { UmbContentTypeContainerStructureHelper, @@ -34,7 +34,7 @@ export class UmbContentWorkspaceViewEditElement extends UmbLitElement implements private _routes: UmbRoute[] = []; @state() - private _tabs?: Array; + private _tabs?: Array; @state() private _routerPath?: string; @@ -64,7 +64,7 @@ export class UmbContentWorkspaceViewEditElement extends UmbLitElement implements this._tabsStructureHelper.setIsRoot(true); this._tabsStructureHelper.setContainerChildType('Tab'); this.observe( - this._tabsStructureHelper.mergedContainers, + this._tabsStructureHelper.childContainers, (tabs) => { this._tabs = tabs; this.#createRoutes(); @@ -117,7 +117,7 @@ export class UmbContentWorkspaceViewEditElement extends UmbLitElement implements path, component: () => import('./content-editor-tab.element.js'), setup: (component) => { - (component as UmbContentWorkspaceViewEditTabElement).containerId = tab.id; + (component as UmbContentWorkspaceViewEditTabElement).containerId = tab.ownerId ?? tab.ids[0]; }, }); this.#createViewContext(path); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/auth/auth-flow.ts b/src/Umbraco.Web.UI.Client/src/packages/core/auth/auth-flow.ts index 8c10231db6..0e02546423 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/auth/auth-flow.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/auth/auth-flow.ts @@ -236,7 +236,7 @@ export class UmbAuthFlow { * @returns true if the user is logged in, false otherwise. */ isAuthorized(): boolean { - return !!this.#tokenResponse; + return !!this.#tokenResponse.getValue(); } /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/client.gen.ts b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/client.gen.ts index fd97b4b69e..ee0c0bd98e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/client.gen.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/client.gen.ts @@ -1,7 +1,7 @@ // This file is auto-generated by @hey-api/openapi-ts import type { ClientOptions } from './types.gen'; -import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from '@hey-api/client-fetch'; +import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from './client'; /** * The `createClientConfig()` function will be called on client initialization diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/client/client.gen.ts b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/client/client.gen.ts new file mode 100644 index 0000000000..0c60a9ab77 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/client/client.gen.ts @@ -0,0 +1,240 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { createSseClient } from '../core/serverSentEvents.gen'; +import type { HttpMethod } from '../core/types.gen'; +import type { + Client, + Config, + RequestOptions, + ResolvedRequestOptions, +} from './types.gen'; +import { + buildUrl, + createConfig, + createInterceptors, + getParseAs, + mergeConfigs, + mergeHeaders, + setAuthParams, +} from './utils.gen'; + +type ReqInit = Omit & { + body?: any; + headers: ReturnType; +}; + +export const createClient = (config: Config = {}): Client => { + let _config = mergeConfigs(createConfig(), config); + + const getConfig = (): Config => ({ ..._config }); + + const setConfig = (config: Config): Config => { + _config = mergeConfigs(_config, config); + return getConfig(); + }; + + const interceptors = createInterceptors< + Request, + Response, + unknown, + ResolvedRequestOptions + >(); + + const beforeRequest = async (options: RequestOptions) => { + const opts = { + ..._config, + ...options, + fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, + headers: mergeHeaders(_config.headers, options.headers), + serializedBody: undefined, + }; + + if (opts.security) { + await setAuthParams({ + ...opts, + security: opts.security, + }); + } + + if (opts.requestValidator) { + await opts.requestValidator(opts); + } + + if (opts.body && opts.bodySerializer) { + opts.serializedBody = opts.bodySerializer(opts.body); + } + + // remove Content-Type header if body is empty to avoid sending invalid requests + if (opts.serializedBody === undefined || opts.serializedBody === '') { + opts.headers.delete('Content-Type'); + } + + const url = buildUrl(opts); + + return { opts, url }; + }; + + const request: Client['request'] = async (options) => { + // @ts-expect-error + const { opts, url } = await beforeRequest(options); + const requestInit: ReqInit = { + redirect: 'follow', + ...opts, + body: opts.serializedBody, + }; + + let request = new Request(url, requestInit); + + for (const fn of interceptors.request._fns) { + if (fn) { + request = await fn(request, opts); + } + } + + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = opts.fetch!; + let response = await _fetch(request); + + for (const fn of interceptors.response._fns) { + if (fn) { + response = await fn(response, request, opts); + } + } + + const result = { + request, + response, + }; + + if (response.ok) { + if ( + response.status === 204 || + response.headers.get('Content-Length') === '0' + ) { + return opts.responseStyle === 'data' + ? {} + : { + data: {}, + ...result, + }; + } + + const parseAs = + (opts.parseAs === 'auto' + ? getParseAs(response.headers.get('Content-Type')) + : opts.parseAs) ?? 'json'; + + let data: any; + switch (parseAs) { + case 'arrayBuffer': + case 'blob': + case 'formData': + case 'json': + case 'text': + data = await response[parseAs](); + break; + case 'stream': + return opts.responseStyle === 'data' + ? response.body + : { + data: response.body, + ...result, + }; + } + + if (parseAs === 'json') { + if (opts.responseValidator) { + await opts.responseValidator(data); + } + + if (opts.responseTransformer) { + data = await opts.responseTransformer(data); + } + } + + return opts.responseStyle === 'data' + ? data + : { + data, + ...result, + }; + } + + const textError = await response.text(); + let jsonError: unknown; + + try { + jsonError = JSON.parse(textError); + } catch { + // noop + } + + const error = jsonError ?? textError; + let finalError = error; + + for (const fn of interceptors.error._fns) { + if (fn) { + finalError = (await fn(error, response, request, opts)) as string; + } + } + + finalError = finalError || ({} as string); + + if (opts.throwOnError) { + throw finalError; + } + + // TODO: we probably want to return error and improve types + return opts.responseStyle === 'data' + ? undefined + : { + error: finalError, + ...result, + }; + }; + + const makeMethodFn = + (method: Uppercase) => (options: RequestOptions) => + request({ ...options, method }); + + const makeSseFn = + (method: Uppercase) => async (options: RequestOptions) => { + const { opts, url } = await beforeRequest(options); + return createSseClient({ + ...opts, + body: opts.body as BodyInit | null | undefined, + headers: opts.headers as unknown as Record, + method, + url, + }); + }; + + return { + buildUrl, + connect: makeMethodFn('CONNECT'), + delete: makeMethodFn('DELETE'), + get: makeMethodFn('GET'), + getConfig, + head: makeMethodFn('HEAD'), + interceptors, + options: makeMethodFn('OPTIONS'), + patch: makeMethodFn('PATCH'), + post: makeMethodFn('POST'), + put: makeMethodFn('PUT'), + request, + setConfig, + sse: { + connect: makeSseFn('CONNECT'), + delete: makeSseFn('DELETE'), + get: makeSseFn('GET'), + head: makeSseFn('HEAD'), + options: makeSseFn('OPTIONS'), + patch: makeSseFn('PATCH'), + post: makeSseFn('POST'), + put: makeSseFn('PUT'), + trace: makeSseFn('TRACE'), + }, + trace: makeMethodFn('TRACE'), + } as Client; +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/client/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/client/index.ts new file mode 100644 index 0000000000..318a84b6a8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/client/index.ts @@ -0,0 +1,25 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { Auth } from '../core/auth.gen'; +export type { QuerySerializerOptions } from '../core/bodySerializer.gen'; +export { + formDataBodySerializer, + jsonBodySerializer, + urlSearchParamsBodySerializer, +} from '../core/bodySerializer.gen'; +export { buildClientParams } from '../core/params.gen'; +export { createClient } from './client.gen'; +export type { + Client, + ClientOptions, + Config, + CreateClientConfig, + Options, + OptionsLegacyParser, + RequestOptions, + RequestResult, + ResolvedRequestOptions, + ResponseStyle, + TDataShape, +} from './types.gen'; +export { createConfig, mergeHeaders } from './utils.gen'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/client/types.gen.ts b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/client/types.gen.ts new file mode 100644 index 0000000000..5bde93851e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/client/types.gen.ts @@ -0,0 +1,268 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Auth } from '../core/auth.gen'; +import type { + ServerSentEventsOptions, + ServerSentEventsResult, +} from '../core/serverSentEvents.gen'; +import type { + Client as CoreClient, + Config as CoreConfig, +} from '../core/types.gen'; +import type { Middleware } from './utils.gen'; + +export type ResponseStyle = 'data' | 'fields'; + +export interface Config + extends Omit, + CoreConfig { + /** + * Base URL for all requests made by this client. + */ + baseUrl?: T['baseUrl']; + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: (request: Request) => ReturnType; + /** + * Please don't use the Fetch client for Next.js applications. The `next` + * options won't have any effect. + * + * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead. + */ + next?: never; + /** + * Return the response data parsed in a specified format. By default, `auto` + * will infer the appropriate method from the `Content-Type` response header. + * You can override this behavior with any of the {@link Body} methods. + * Select `stream` if you don't want to parse response data at all. + * + * @default 'auto' + */ + parseAs?: + | 'arrayBuffer' + | 'auto' + | 'blob' + | 'formData' + | 'json' + | 'stream' + | 'text'; + /** + * Should we return only data or multiple fields (data, error, response, etc.)? + * + * @default 'fields' + */ + responseStyle?: ResponseStyle; + /** + * Throw an error instead of returning it in the response? + * + * @default false + */ + throwOnError?: T['throwOnError']; +} + +export interface RequestOptions< + TData = unknown, + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, +> extends Config<{ + responseStyle: TResponseStyle; + throwOnError: ThrowOnError; + }>, + Pick< + ServerSentEventsOptions, + | 'onSseError' + | 'onSseEvent' + | 'sseDefaultRetryDelay' + | 'sseMaxRetryAttempts' + | 'sseMaxRetryDelay' + > { + /** + * Any body that you want to add to your request. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} + */ + body?: unknown; + path?: Record; + query?: Record; + /** + * Security mechanism(s) to use for the request. + */ + security?: ReadonlyArray; + url: Url; +} + +export interface ResolvedRequestOptions< + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, +> extends RequestOptions { + serializedBody?: string; +} + +export type RequestResult< + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = 'fields', +> = ThrowOnError extends true + ? Promise< + TResponseStyle extends 'data' + ? TData extends Record + ? TData[keyof TData] + : TData + : { + data: TData extends Record + ? TData[keyof TData] + : TData; + request: Request; + response: Response; + } + > + : Promise< + TResponseStyle extends 'data' + ? + | (TData extends Record + ? TData[keyof TData] + : TData) + | undefined + : ( + | { + data: TData extends Record + ? TData[keyof TData] + : TData; + error: undefined; + } + | { + data: undefined; + error: TError extends Record + ? TError[keyof TError] + : TError; + } + ) & { + request: Request; + response: Response; + } + >; + +export interface ClientOptions { + baseUrl?: string; + responseStyle?: ResponseStyle; + throwOnError?: boolean; +} + +type MethodFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'>, +) => RequestResult; + +type SseFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'>, +) => Promise>; + +type RequestFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'> & + Pick< + Required>, + 'method' + >, +) => RequestResult; + +type BuildUrlFn = < + TData extends { + body?: unknown; + path?: Record; + query?: Record; + url: string; + }, +>( + options: Pick & Options, +) => string; + +export type Client = CoreClient< + RequestFn, + Config, + MethodFn, + BuildUrlFn, + SseFn +> & { + interceptors: Middleware; +}; + +/** + * The `createClientConfig()` function will be called on client initialization + * and the returned object will become the client's initial configuration. + * + * You may want to initialize your client this way instead of calling + * `setConfig()`. This is useful for example if you're using Next.js + * to ensure your client always has the correct values. + */ +export type CreateClientConfig = ( + override?: Config, +) => Config & T>; + +export interface TDataShape { + body?: unknown; + headers?: unknown; + path?: unknown; + query?: unknown; + url: string; +} + +type OmitKeys = Pick>; + +export type Options< + TData extends TDataShape = TDataShape, + ThrowOnError extends boolean = boolean, + TResponse = unknown, + TResponseStyle extends ResponseStyle = 'fields', +> = OmitKeys< + RequestOptions, + 'body' | 'path' | 'query' | 'url' +> & + Omit; + +export type OptionsLegacyParser< + TData = unknown, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = 'fields', +> = TData extends { body?: any } + ? TData extends { headers?: any } + ? OmitKeys< + RequestOptions, + 'body' | 'headers' | 'url' + > & + TData + : OmitKeys< + RequestOptions, + 'body' | 'url' + > & + TData & + Pick, 'headers'> + : TData extends { headers?: any } + ? OmitKeys< + RequestOptions, + 'headers' | 'url' + > & + TData & + Pick, 'body'> + : OmitKeys, 'url'> & + TData; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/client/utils.gen.ts b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/client/utils.gen.ts new file mode 100644 index 0000000000..a47509522b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/client/utils.gen.ts @@ -0,0 +1,327 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { getAuthToken } from '../core/auth.gen'; +import type { QuerySerializerOptions } from '../core/bodySerializer.gen'; +import { jsonBodySerializer } from '../core/bodySerializer.gen'; +import { + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from '../core/pathSerializer.gen'; +import { getUrl } from '../core/utils.gen'; +import type { Client, ClientOptions, Config, RequestOptions } from './types.gen'; + +export const createQuerySerializer = ({ + allowReserved, + array, + object, +}: QuerySerializerOptions = {}) => { + const querySerializer = (queryParams: T) => { + const search: string[] = []; + if (queryParams && typeof queryParams === 'object') { + for (const name in queryParams) { + const value = queryParams[name]; + + if (value === undefined || value === null) { + continue; + } + + if (Array.isArray(value)) { + const serializedArray = serializeArrayParam({ + allowReserved, + explode: true, + name, + style: 'form', + value, + ...array, + }); + if (serializedArray) search.push(serializedArray); + } else if (typeof value === 'object') { + const serializedObject = serializeObjectParam({ + allowReserved, + explode: true, + name, + style: 'deepObject', + value: value as Record, + ...object, + }); + if (serializedObject) search.push(serializedObject); + } else { + const serializedPrimitive = serializePrimitiveParam({ + allowReserved, + name, + value: value as string, + }); + if (serializedPrimitive) search.push(serializedPrimitive); + } + } + } + return search.join('&'); + }; + return querySerializer; +}; + +/** + * Infers parseAs value from provided Content-Type header. + */ +export const getParseAs = ( + contentType: string | null, +): Exclude => { + if (!contentType) { + // If no Content-Type header is provided, the best we can do is return the raw response body, + // which is effectively the same as the 'stream' option. + return 'stream'; + } + + const cleanContent = contentType.split(';')[0]?.trim(); + + if (!cleanContent) { + return; + } + + if ( + cleanContent.startsWith('application/json') || + cleanContent.endsWith('+json') + ) { + return 'json'; + } + + if (cleanContent === 'multipart/form-data') { + return 'formData'; + } + + if ( + ['application/', 'audio/', 'image/', 'video/'].some((type) => + cleanContent.startsWith(type), + ) + ) { + return 'blob'; + } + + if (cleanContent.startsWith('text/')) { + return 'text'; + } + + return; +}; + +const checkForExistence = ( + options: Pick & { + headers: Headers; + }, + name?: string, +): boolean => { + if (!name) { + return false; + } + if ( + options.headers.has(name) || + options.query?.[name] || + options.headers.get('Cookie')?.includes(`${name}=`) + ) { + return true; + } + return false; +}; + +export const setAuthParams = async ({ + security, + ...options +}: Pick, 'security'> & + Pick & { + headers: Headers; + }) => { + for (const auth of security) { + if (checkForExistence(options, auth.name)) { + continue; + } + + const token = await getAuthToken(auth, options.auth); + + if (!token) { + continue; + } + + const name = auth.name ?? 'Authorization'; + + switch (auth.in) { + case 'query': + if (!options.query) { + options.query = {}; + } + options.query[name] = token; + break; + case 'cookie': + options.headers.append('Cookie', `${name}=${token}`); + break; + case 'header': + default: + options.headers.set(name, token); + break; + } + } +}; + +export const buildUrl: Client['buildUrl'] = (options) => + getUrl({ + baseUrl: options.baseUrl as string, + path: options.path, + query: options.query, + querySerializer: + typeof options.querySerializer === 'function' + ? options.querySerializer + : createQuerySerializer(options.querySerializer), + url: options.url, + }); + +export const mergeConfigs = (a: Config, b: Config): Config => { + const config = { ...a, ...b }; + if (config.baseUrl?.endsWith('/')) { + config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); + } + config.headers = mergeHeaders(a.headers, b.headers); + return config; +}; + +export const mergeHeaders = ( + ...headers: Array['headers'] | undefined> +): Headers => { + const mergedHeaders = new Headers(); + for (const header of headers) { + if (!header || typeof header !== 'object') { + continue; + } + + const iterator = + header instanceof Headers ? header.entries() : Object.entries(header); + + for (const [key, value] of iterator) { + if (value === null) { + mergedHeaders.delete(key); + } else if (Array.isArray(value)) { + for (const v of value) { + mergedHeaders.append(key, v as string); + } + } else if (value !== undefined) { + // assume object headers are meant to be JSON stringified, i.e. their + // content value in OpenAPI specification is 'application/json' + mergedHeaders.set( + key, + typeof value === 'object' ? JSON.stringify(value) : (value as string), + ); + } + } + } + return mergedHeaders; +}; + +type ErrInterceptor = ( + error: Err, + response: Res, + request: Req, + options: Options, +) => Err | Promise; + +type ReqInterceptor = ( + request: Req, + options: Options, +) => Req | Promise; + +type ResInterceptor = ( + response: Res, + request: Req, + options: Options, +) => Res | Promise; + +class Interceptors { + _fns: (Interceptor | null)[]; + + constructor() { + this._fns = []; + } + + clear() { + this._fns = []; + } + + getInterceptorIndex(id: number | Interceptor): number { + if (typeof id === 'number') { + return this._fns[id] ? id : -1; + } else { + return this._fns.indexOf(id); + } + } + exists(id: number | Interceptor) { + const index = this.getInterceptorIndex(id); + return !!this._fns[index]; + } + + eject(id: number | Interceptor) { + const index = this.getInterceptorIndex(id); + if (this._fns[index]) { + this._fns[index] = null; + } + } + + update(id: number | Interceptor, fn: Interceptor) { + const index = this.getInterceptorIndex(id); + if (this._fns[index]) { + this._fns[index] = fn; + return id; + } else { + return false; + } + } + + use(fn: Interceptor) { + this._fns = [...this._fns, fn]; + return this._fns.length - 1; + } +} + +// `createInterceptors()` response, meant for external use as it does not +// expose internals +export interface Middleware { + error: Pick< + Interceptors>, + 'eject' | 'use' + >; + request: Pick>, 'eject' | 'use'>; + response: Pick< + Interceptors>, + 'eject' | 'use' + >; +} + +// do not add `Middleware` as return type so we can use _fns internally +export const createInterceptors = () => ({ + error: new Interceptors>(), + request: new Interceptors>(), + response: new Interceptors>(), +}); + +const defaultQuerySerializer = createQuerySerializer({ + allowReserved: false, + array: { + explode: true, + style: 'form', + }, + object: { + explode: true, + style: 'deepObject', + }, +}); + +const defaultHeaders = { + 'Content-Type': 'application/json', +}; + +export const createConfig = ( + override: Config & T> = {}, +): Config & T> => ({ + ...jsonBodySerializer, + headers: defaultHeaders, + parseAs: 'auto', + querySerializer: defaultQuerySerializer, + ...override, +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/auth.gen.ts b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/auth.gen.ts new file mode 100644 index 0000000000..f8a73266f9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/auth.gen.ts @@ -0,0 +1,42 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type AuthToken = string | undefined; + +export interface Auth { + /** + * Which part of the request do we use to send the auth? + * + * @default 'header' + */ + in?: 'header' | 'query' | 'cookie'; + /** + * Header or query parameter name. + * + * @default 'Authorization' + */ + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} + +export const getAuthToken = async ( + auth: Auth, + callback: ((auth: Auth) => Promise | AuthToken) | AuthToken, +): Promise => { + const token = + typeof callback === 'function' ? await callback(auth) : callback; + + if (!token) { + return; + } + + if (auth.scheme === 'bearer') { + return `Bearer ${token}`; + } + + if (auth.scheme === 'basic') { + return `Basic ${btoa(token)}`; + } + + return token; +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/bodySerializer.gen.ts b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/bodySerializer.gen.ts new file mode 100644 index 0000000000..49cd8925e3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/bodySerializer.gen.ts @@ -0,0 +1,92 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { + ArrayStyle, + ObjectStyle, + SerializerOptions, +} from './pathSerializer.gen'; + +export type QuerySerializer = (query: Record) => string; + +export type BodySerializer = (body: any) => any; + +export interface QuerySerializerOptions { + allowReserved?: boolean; + array?: SerializerOptions; + object?: SerializerOptions; +} + +const serializeFormDataPair = ( + data: FormData, + key: string, + value: unknown, +): void => { + if (typeof value === 'string' || value instanceof Blob) { + data.append(key, value); + } else if (value instanceof Date) { + data.append(key, value.toISOString()); + } else { + data.append(key, JSON.stringify(value)); + } +}; + +const serializeUrlSearchParamsPair = ( + data: URLSearchParams, + key: string, + value: unknown, +): void => { + if (typeof value === 'string') { + data.append(key, value); + } else { + data.append(key, JSON.stringify(value)); + } +}; + +export const formDataBodySerializer = { + bodySerializer: | Array>>( + body: T, + ): FormData => { + const data = new FormData(); + + Object.entries(body).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeFormDataPair(data, key, v)); + } else { + serializeFormDataPair(data, key, value); + } + }); + + return data; + }, +}; + +export const jsonBodySerializer = { + bodySerializer: (body: T): string => + JSON.stringify(body, (_key, value) => + typeof value === 'bigint' ? value.toString() : value, + ), +}; + +export const urlSearchParamsBodySerializer = { + bodySerializer: | Array>>( + body: T, + ): string => { + const data = new URLSearchParams(); + + Object.entries(body).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); + } else { + serializeUrlSearchParamsPair(data, key, value); + } + }); + + return data.toString(); + }, +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/params.gen.ts b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/params.gen.ts new file mode 100644 index 0000000000..71c88e852b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/params.gen.ts @@ -0,0 +1,153 @@ +// This file is auto-generated by @hey-api/openapi-ts + +type Slot = 'body' | 'headers' | 'path' | 'query'; + +export type Field = + | { + in: Exclude; + /** + * Field name. This is the name we want the user to see and use. + */ + key: string; + /** + * Field mapped name. This is the name we want to use in the request. + * If omitted, we use the same value as `key`. + */ + map?: string; + } + | { + in: Extract; + /** + * Key isn't required for bodies. + */ + key?: string; + map?: string; + }; + +export interface Fields { + allowExtra?: Partial>; + args?: ReadonlyArray; +} + +export type FieldsConfig = ReadonlyArray; + +const extraPrefixesMap: Record = { + $body_: 'body', + $headers_: 'headers', + $path_: 'path', + $query_: 'query', +}; +const extraPrefixes = Object.entries(extraPrefixesMap); + +type KeyMap = Map< + string, + { + in: Slot; + map?: string; + } +>; + +const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => { + if (!map) { + map = new Map(); + } + + for (const config of fields) { + if ('in' in config) { + if (config.key) { + map.set(config.key, { + in: config.in, + map: config.map, + }); + } + } else if (config.args) { + buildKeyMap(config.args, map); + } + } + + return map; +}; + +interface Params { + body: unknown; + headers: Record; + path: Record; + query: Record; +} + +const stripEmptySlots = (params: Params) => { + for (const [slot, value] of Object.entries(params)) { + if (value && typeof value === 'object' && !Object.keys(value).length) { + delete params[slot as Slot]; + } + } +}; + +export const buildClientParams = ( + args: ReadonlyArray, + fields: FieldsConfig, +) => { + const params: Params = { + body: {}, + headers: {}, + path: {}, + query: {}, + }; + + const map = buildKeyMap(fields); + + let config: FieldsConfig[number] | undefined; + + for (const [index, arg] of args.entries()) { + if (fields[index]) { + config = fields[index]; + } + + if (!config) { + continue; + } + + if ('in' in config) { + if (config.key) { + const field = map.get(config.key)!; + const name = field.map || config.key; + (params[field.in] as Record)[name] = arg; + } else { + params.body = arg; + } + } else { + for (const [key, value] of Object.entries(arg ?? {})) { + const field = map.get(key); + + if (field) { + const name = field.map || key; + (params[field.in] as Record)[name] = value; + } else { + const extra = extraPrefixes.find(([prefix]) => + key.startsWith(prefix), + ); + + if (extra) { + const [prefix, slot] = extra; + (params[slot] as Record)[ + key.slice(prefix.length) + ] = value; + } else { + for (const [slot, allowed] of Object.entries( + config.allowExtra ?? {}, + )) { + if (allowed) { + (params[slot as Slot] as Record)[key] = value; + break; + } + } + } + } + } + } + } + + stripEmptySlots(params); + + return params; +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/pathSerializer.gen.ts b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/pathSerializer.gen.ts new file mode 100644 index 0000000000..8d99931047 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/pathSerializer.gen.ts @@ -0,0 +1,181 @@ +// This file is auto-generated by @hey-api/openapi-ts + +interface SerializeOptions + extends SerializePrimitiveOptions, + SerializerOptions {} + +interface SerializePrimitiveOptions { + allowReserved?: boolean; + name: string; +} + +export interface SerializerOptions { + /** + * @default true + */ + explode: boolean; + style: T; +} + +export type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; +export type ArraySeparatorStyle = ArrayStyle | MatrixStyle; +type MatrixStyle = 'label' | 'matrix' | 'simple'; +export type ObjectStyle = 'form' | 'deepObject'; +type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; + +interface SerializePrimitiveParam extends SerializePrimitiveOptions { + value: string; +} + +export const separatorArrayExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } +}; + +export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'form': + return ','; + case 'pipeDelimited': + return '|'; + case 'spaceDelimited': + return '%20'; + default: + return ','; + } +}; + +export const separatorObjectExplode = (style: ObjectSeparatorStyle) => { + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } +}; + +export const serializeArrayParam = ({ + allowReserved, + explode, + name, + style, + value, +}: SerializeOptions & { + value: unknown[]; +}) => { + if (!explode) { + const joinedValues = ( + allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) + ).join(separatorArrayNoExplode(style)); + switch (style) { + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + case 'simple': + return joinedValues; + default: + return `${name}=${joinedValues}`; + } + } + + const separator = separatorArrayExplode(style); + const joinedValues = value + .map((v) => { + if (style === 'label' || style === 'simple') { + return allowReserved ? v : encodeURIComponent(v as string); + } + + return serializePrimitiveParam({ + allowReserved, + name, + value: v as string, + }); + }) + .join(separator); + return style === 'label' || style === 'matrix' + ? separator + joinedValues + : joinedValues; +}; + +export const serializePrimitiveParam = ({ + allowReserved, + name, + value, +}: SerializePrimitiveParam) => { + if (value === undefined || value === null) { + return ''; + } + + if (typeof value === 'object') { + throw new Error( + 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', + ); + } + + return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; +}; + +export const serializeObjectParam = ({ + allowReserved, + explode, + name, + style, + value, + valueOnly, +}: SerializeOptions & { + value: Record | Date; + valueOnly?: boolean; +}) => { + if (value instanceof Date) { + return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`; + } + + if (style !== 'deepObject' && !explode) { + let values: string[] = []; + Object.entries(value).forEach(([key, v]) => { + values = [ + ...values, + key, + allowReserved ? (v as string) : encodeURIComponent(v as string), + ]; + }); + const joinedValues = values.join(','); + switch (style) { + case 'form': + return `${name}=${joinedValues}`; + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + default: + return joinedValues; + } + } + + const separator = separatorObjectExplode(style); + const joinedValues = Object.entries(value) + .map(([key, v]) => + serializePrimitiveParam({ + allowReserved, + name: style === 'deepObject' ? `${name}[${key}]` : key, + value: v as string, + }), + ) + .join(separator); + return style === 'label' || style === 'matrix' + ? separator + joinedValues + : joinedValues; +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/serverSentEvents.gen.ts b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/serverSentEvents.gen.ts new file mode 100644 index 0000000000..01b5818fdf --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/serverSentEvents.gen.ts @@ -0,0 +1,237 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Config } from './types.gen'; + +export type ServerSentEventsOptions = Omit< + RequestInit, + 'method' +> & + Pick & { + /** + * Callback invoked when a network or parsing error occurs during streaming. + * + * This option applies only if the endpoint returns a stream of events. + * + * @param error The error that occurred. + */ + onSseError?: (error: unknown) => void; + /** + * Callback invoked when an event is streamed from the server. + * + * This option applies only if the endpoint returns a stream of events. + * + * @param event Event streamed from the server. + * @returns Nothing (void). + */ + onSseEvent?: (event: StreamEvent) => void; + /** + * Default retry delay in milliseconds. + * + * This option applies only if the endpoint returns a stream of events. + * + * @default 3000 + */ + sseDefaultRetryDelay?: number; + /** + * Maximum number of retry attempts before giving up. + */ + sseMaxRetryAttempts?: number; + /** + * Maximum retry delay in milliseconds. + * + * Applies only when exponential backoff is used. + * + * This option applies only if the endpoint returns a stream of events. + * + * @default 30000 + */ + sseMaxRetryDelay?: number; + /** + * Optional sleep function for retry backoff. + * + * Defaults to using `setTimeout`. + */ + sseSleepFn?: (ms: number) => Promise; + url: string; + }; + +export interface StreamEvent { + data: TData; + event?: string; + id?: string; + retry?: number; +} + +export type ServerSentEventsResult< + TData = unknown, + TReturn = void, + TNext = unknown, +> = { + stream: AsyncGenerator< + TData extends Record ? TData[keyof TData] : TData, + TReturn, + TNext + >; +}; + +export const createSseClient = ({ + onSseError, + onSseEvent, + responseTransformer, + responseValidator, + sseDefaultRetryDelay, + sseMaxRetryAttempts, + sseMaxRetryDelay, + sseSleepFn, + url, + ...options +}: ServerSentEventsOptions): ServerSentEventsResult => { + let lastEventId: string | undefined; + + const sleep = + sseSleepFn ?? + ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms))); + + const createStream = async function* () { + let retryDelay: number = sseDefaultRetryDelay ?? 3000; + let attempt = 0; + const signal = options.signal ?? new AbortController().signal; + + while (true) { + if (signal.aborted) break; + + attempt++; + + const headers = + options.headers instanceof Headers + ? options.headers + : new Headers(options.headers as Record | undefined); + + if (lastEventId !== undefined) { + headers.set('Last-Event-ID', lastEventId); + } + + try { + const response = await fetch(url, { ...options, headers, signal }); + + if (!response.ok) + throw new Error( + `SSE failed: ${response.status} ${response.statusText}`, + ); + + if (!response.body) throw new Error('No body in SSE response'); + + const reader = response.body + .pipeThrough(new TextDecoderStream()) + .getReader(); + + let buffer = ''; + + const abortHandler = () => { + try { + reader.cancel(); + } catch { + // noop + } + }; + + signal.addEventListener('abort', abortHandler); + + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + buffer += value; + + const chunks = buffer.split('\n\n'); + buffer = chunks.pop() ?? ''; + + for (const chunk of chunks) { + const lines = chunk.split('\n'); + const dataLines: Array = []; + let eventName: string | undefined; + + for (const line of lines) { + if (line.startsWith('data:')) { + dataLines.push(line.replace(/^data:\s*/, '')); + } else if (line.startsWith('event:')) { + eventName = line.replace(/^event:\s*/, ''); + } else if (line.startsWith('id:')) { + lastEventId = line.replace(/^id:\s*/, ''); + } else if (line.startsWith('retry:')) { + const parsed = Number.parseInt( + line.replace(/^retry:\s*/, ''), + 10, + ); + if (!Number.isNaN(parsed)) { + retryDelay = parsed; + } + } + } + + let data: unknown; + let parsedJson = false; + + if (dataLines.length) { + const rawData = dataLines.join('\n'); + try { + data = JSON.parse(rawData); + parsedJson = true; + } catch { + data = rawData; + } + } + + if (parsedJson) { + if (responseValidator) { + await responseValidator(data); + } + + if (responseTransformer) { + data = await responseTransformer(data); + } + } + + onSseEvent?.({ + data, + event: eventName, + id: lastEventId, + retry: retryDelay, + }); + + if (dataLines.length) { + yield data as any; + } + } + } + } finally { + signal.removeEventListener('abort', abortHandler); + reader.releaseLock(); + } + + break; // exit loop on normal completion + } catch (error) { + // connection failed or aborted; retry after delay + onSseError?.(error); + + if ( + sseMaxRetryAttempts !== undefined && + attempt >= sseMaxRetryAttempts + ) { + break; // stop after firing error + } + + // exponential backoff: double retry each attempt, cap at 30s + const backoff = Math.min( + retryDelay * 2 ** (attempt - 1), + sseMaxRetryDelay ?? 30000, + ); + await sleep(backoff); + } + } + }; + + const stream = createStream(); + + return { stream }; +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/types.gen.ts b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/types.gen.ts new file mode 100644 index 0000000000..643c070c9d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/types.gen.ts @@ -0,0 +1,118 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Auth, AuthToken } from './auth.gen'; +import type { + BodySerializer, + QuerySerializer, + QuerySerializerOptions, +} from './bodySerializer.gen'; + +export type HttpMethod = + | 'connect' + | 'delete' + | 'get' + | 'head' + | 'options' + | 'patch' + | 'post' + | 'put' + | 'trace'; + +export type Client< + RequestFn = never, + Config = unknown, + MethodFn = never, + BuildUrlFn = never, + SseFn = never, +> = { + /** + * Returns the final request URL. + */ + buildUrl: BuildUrlFn; + getConfig: () => Config; + request: RequestFn; + setConfig: (config: Config) => Config; +} & { + [K in HttpMethod]: MethodFn; +} & ([SseFn] extends [never] + ? { sse?: never } + : { sse: { [K in HttpMethod]: SseFn } }); + +export interface Config { + /** + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. + */ + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; + /** + * A function for serializing request body parameter. By default, + * {@link JSON.stringify()} will be used. + */ + bodySerializer?: BodySerializer | null; + /** + * An object containing any HTTP headers that you want to pre-populate your + * `Headers` object with. + * + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} + */ + headers?: + | RequestInit['headers'] + | Record< + string, + | string + | number + | boolean + | (string | number | boolean)[] + | null + | undefined + | unknown + >; + /** + * The request method. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} + */ + method?: Uppercase; + /** + * A function for serializing request query parameters. By default, arrays + * will be exploded in form style, objects will be exploded in deepObject + * style, and reserved characters are percent-encoded. + * + * This method will have no effect if the native `paramsSerializer()` Axios + * API function is used. + * + * {@link https://swagger.io/docs/specification/serialization/#query View examples} + */ + querySerializer?: QuerySerializer | QuerySerializerOptions; + /** + * A function validating request data. This is useful if you want to ensure + * the request conforms to the desired shape, so it can be safely sent to + * the server. + */ + requestValidator?: (data: unknown) => Promise; + /** + * A function transforming response data before it's returned. This is useful + * for post-processing data, e.g. converting ISO strings into Date objects. + */ + responseTransformer?: (data: unknown) => Promise; + /** + * A function validating response data. This is useful if you want to ensure + * the response conforms to the desired shape, so it can be safely passed to + * the transformers and returned to the user. + */ + responseValidator?: (data: unknown) => Promise; +} + +type IsExactlyNeverOrNeverUndefined = [T] extends [never] + ? true + : [T] extends [never | undefined] + ? [undefined] extends [T] + ? false + : true + : false; + +export type OmitNever> = { + [K in keyof T as IsExactlyNeverOrNeverUndefined extends true + ? never + : K]: T[K]; +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/utils.gen.ts b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/utils.gen.ts new file mode 100644 index 0000000000..ac31396fea --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/core/utils.gen.ts @@ -0,0 +1,114 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { QuerySerializer } from './bodySerializer.gen'; +import { + type ArraySeparatorStyle, + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from './pathSerializer.gen'; + +export interface PathSerializer { + path: Record; + url: string; +} + +export const PATH_PARAM_RE = /\{[^{}]+\}/g; + +export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { + let url = _url; + const matches = _url.match(PATH_PARAM_RE); + if (matches) { + for (const match of matches) { + let explode = false; + let name = match.substring(1, match.length - 1); + let style: ArraySeparatorStyle = 'simple'; + + if (name.endsWith('*')) { + explode = true; + name = name.substring(0, name.length - 1); + } + + if (name.startsWith('.')) { + name = name.substring(1); + style = 'label'; + } else if (name.startsWith(';')) { + name = name.substring(1); + style = 'matrix'; + } + + const value = path[name]; + + if (value === undefined || value === null) { + continue; + } + + if (Array.isArray(value)) { + url = url.replace( + match, + serializeArrayParam({ explode, name, style, value }), + ); + continue; + } + + if (typeof value === 'object') { + url = url.replace( + match, + serializeObjectParam({ + explode, + name, + style, + value: value as Record, + valueOnly: true, + }), + ); + continue; + } + + if (style === 'matrix') { + url = url.replace( + match, + `;${serializePrimitiveParam({ + name, + value: value as string, + })}`, + ); + continue; + } + + const replaceValue = encodeURIComponent( + style === 'label' ? `.${value as string}` : (value as string), + ); + url = url.replace(match, replaceValue); + } + } + return url; +}; + +export const getUrl = ({ + baseUrl, + path, + query, + querySerializer, + url: _url, +}: { + baseUrl?: string; + path?: Record; + query?: Record; + querySerializer: QuerySerializer; + url: string; +}) => { + const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; + let url = (baseUrl ?? '') + pathUrl; + if (path) { + url = defaultPathSerializer({ path, url }); + } + let search = query ? querySerializer(query) : ''; + if (search.startsWith('?')) { + search = search.substring(1); + } + if (search) { + url += `?${search}`; + } + return url; +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/sdk.gen.ts b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/sdk.gen.ts index 62ef1c6b23..5e723adbf2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/sdk.gen.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/sdk.gen.ts @@ -1,6 +1,6 @@ // This file is auto-generated by @hey-api/openapi-ts -import { type Options as ClientOptions, type TDataShape, type Client, formDataBodySerializer } from '@hey-api/client-fetch'; +import { type Options as ClientOptions, type TDataShape, type Client, formDataBodySerializer } from './client'; import type { GetCultureData, GetCultureResponses, GetCultureErrors, PostDataTypeData, PostDataTypeResponses, PostDataTypeErrors, DeleteDataTypeByIdData, DeleteDataTypeByIdResponses, DeleteDataTypeByIdErrors, GetDataTypeByIdData, GetDataTypeByIdResponses, GetDataTypeByIdErrors, PutDataTypeByIdData, PutDataTypeByIdResponses, PutDataTypeByIdErrors, PostDataTypeByIdCopyData, PostDataTypeByIdCopyResponses, PostDataTypeByIdCopyErrors, GetDataTypeByIdIsUsedData, GetDataTypeByIdIsUsedResponses, GetDataTypeByIdIsUsedErrors, PutDataTypeByIdMoveData, PutDataTypeByIdMoveResponses, PutDataTypeByIdMoveErrors, GetDataTypeByIdReferencedByData, GetDataTypeByIdReferencedByResponses, GetDataTypeByIdReferencedByErrors, GetDataTypeByIdReferencesData, GetDataTypeByIdReferencesResponses, GetDataTypeByIdReferencesErrors, GetDataTypeConfigurationData, GetDataTypeConfigurationResponses, GetDataTypeConfigurationErrors, PostDataTypeFolderData, PostDataTypeFolderResponses, PostDataTypeFolderErrors, DeleteDataTypeFolderByIdData, DeleteDataTypeFolderByIdResponses, DeleteDataTypeFolderByIdErrors, GetDataTypeFolderByIdData, GetDataTypeFolderByIdResponses, GetDataTypeFolderByIdErrors, PutDataTypeFolderByIdData, PutDataTypeFolderByIdResponses, PutDataTypeFolderByIdErrors, GetFilterDataTypeData, GetFilterDataTypeResponses, GetFilterDataTypeErrors, GetItemDataTypeData, GetItemDataTypeResponses, GetItemDataTypeErrors, GetItemDataTypeSearchData, GetItemDataTypeSearchResponses, GetItemDataTypeSearchErrors, GetTreeDataTypeAncestorsData, GetTreeDataTypeAncestorsResponses, GetTreeDataTypeAncestorsErrors, GetTreeDataTypeChildrenData, GetTreeDataTypeChildrenResponses, GetTreeDataTypeChildrenErrors, GetTreeDataTypeRootData, GetTreeDataTypeRootResponses, GetTreeDataTypeRootErrors, GetTreeDataTypeSiblingsData, GetTreeDataTypeSiblingsResponses, GetTreeDataTypeSiblingsErrors, GetDictionaryData, GetDictionaryResponses, GetDictionaryErrors, PostDictionaryData, PostDictionaryResponses, PostDictionaryErrors, DeleteDictionaryByIdData, DeleteDictionaryByIdResponses, DeleteDictionaryByIdErrors, GetDictionaryByIdData, GetDictionaryByIdResponses, GetDictionaryByIdErrors, PutDictionaryByIdData, PutDictionaryByIdResponses, PutDictionaryByIdErrors, GetDictionaryByIdExportData, GetDictionaryByIdExportResponses, GetDictionaryByIdExportErrors, PutDictionaryByIdMoveData, PutDictionaryByIdMoveResponses, PutDictionaryByIdMoveErrors, PostDictionaryImportData, PostDictionaryImportResponses, PostDictionaryImportErrors, GetItemDictionaryData, GetItemDictionaryResponses, GetItemDictionaryErrors, GetTreeDictionaryAncestorsData, GetTreeDictionaryAncestorsResponses, GetTreeDictionaryAncestorsErrors, GetTreeDictionaryChildrenData, GetTreeDictionaryChildrenResponses, GetTreeDictionaryChildrenErrors, GetTreeDictionaryRootData, GetTreeDictionaryRootResponses, GetTreeDictionaryRootErrors, PostDocumentBlueprintData, PostDocumentBlueprintResponses, PostDocumentBlueprintErrors, DeleteDocumentBlueprintByIdData, DeleteDocumentBlueprintByIdResponses, DeleteDocumentBlueprintByIdErrors, GetDocumentBlueprintByIdData, GetDocumentBlueprintByIdResponses, GetDocumentBlueprintByIdErrors, PutDocumentBlueprintByIdData, PutDocumentBlueprintByIdResponses, PutDocumentBlueprintByIdErrors, PutDocumentBlueprintByIdMoveData, PutDocumentBlueprintByIdMoveResponses, PutDocumentBlueprintByIdMoveErrors, GetDocumentBlueprintByIdScaffoldData, GetDocumentBlueprintByIdScaffoldResponses, GetDocumentBlueprintByIdScaffoldErrors, PostDocumentBlueprintFolderData, PostDocumentBlueprintFolderResponses, PostDocumentBlueprintFolderErrors, DeleteDocumentBlueprintFolderByIdData, DeleteDocumentBlueprintFolderByIdResponses, DeleteDocumentBlueprintFolderByIdErrors, GetDocumentBlueprintFolderByIdData, GetDocumentBlueprintFolderByIdResponses, GetDocumentBlueprintFolderByIdErrors, PutDocumentBlueprintFolderByIdData, PutDocumentBlueprintFolderByIdResponses, PutDocumentBlueprintFolderByIdErrors, PostDocumentBlueprintFromDocumentData, PostDocumentBlueprintFromDocumentResponses, PostDocumentBlueprintFromDocumentErrors, GetItemDocumentBlueprintData, GetItemDocumentBlueprintResponses, GetItemDocumentBlueprintErrors, GetTreeDocumentBlueprintAncestorsData, GetTreeDocumentBlueprintAncestorsResponses, GetTreeDocumentBlueprintAncestorsErrors, GetTreeDocumentBlueprintChildrenData, GetTreeDocumentBlueprintChildrenResponses, GetTreeDocumentBlueprintChildrenErrors, GetTreeDocumentBlueprintRootData, GetTreeDocumentBlueprintRootResponses, GetTreeDocumentBlueprintRootErrors, GetTreeDocumentBlueprintSiblingsData, GetTreeDocumentBlueprintSiblingsResponses, GetTreeDocumentBlueprintSiblingsErrors, PostDocumentTypeData, PostDocumentTypeResponses, PostDocumentTypeErrors, DeleteDocumentTypeByIdData, DeleteDocumentTypeByIdResponses, DeleteDocumentTypeByIdErrors, GetDocumentTypeByIdData, GetDocumentTypeByIdResponses, GetDocumentTypeByIdErrors, PutDocumentTypeByIdData, PutDocumentTypeByIdResponses, PutDocumentTypeByIdErrors, GetDocumentTypeByIdAllowedChildrenData, GetDocumentTypeByIdAllowedChildrenResponses, GetDocumentTypeByIdAllowedChildrenErrors, GetDocumentTypeByIdBlueprintData, GetDocumentTypeByIdBlueprintResponses, GetDocumentTypeByIdBlueprintErrors, GetDocumentTypeByIdCompositionReferencesData, GetDocumentTypeByIdCompositionReferencesResponses, GetDocumentTypeByIdCompositionReferencesErrors, PostDocumentTypeByIdCopyData, PostDocumentTypeByIdCopyResponses, PostDocumentTypeByIdCopyErrors, GetDocumentTypeByIdExportData, GetDocumentTypeByIdExportResponses, GetDocumentTypeByIdExportErrors, PutDocumentTypeByIdImportData, PutDocumentTypeByIdImportResponses, PutDocumentTypeByIdImportErrors, PutDocumentTypeByIdMoveData, PutDocumentTypeByIdMoveResponses, PutDocumentTypeByIdMoveErrors, GetDocumentTypeAllowedAtRootData, GetDocumentTypeAllowedAtRootResponses, GetDocumentTypeAllowedAtRootErrors, PostDocumentTypeAvailableCompositionsData, PostDocumentTypeAvailableCompositionsResponses, PostDocumentTypeAvailableCompositionsErrors, GetDocumentTypeConfigurationData, GetDocumentTypeConfigurationResponses, GetDocumentTypeConfigurationErrors, PostDocumentTypeFolderData, PostDocumentTypeFolderResponses, PostDocumentTypeFolderErrors, DeleteDocumentTypeFolderByIdData, DeleteDocumentTypeFolderByIdResponses, DeleteDocumentTypeFolderByIdErrors, GetDocumentTypeFolderByIdData, GetDocumentTypeFolderByIdResponses, GetDocumentTypeFolderByIdErrors, PutDocumentTypeFolderByIdData, PutDocumentTypeFolderByIdResponses, PutDocumentTypeFolderByIdErrors, PostDocumentTypeImportData, PostDocumentTypeImportResponses, PostDocumentTypeImportErrors, GetItemDocumentTypeData, GetItemDocumentTypeResponses, GetItemDocumentTypeErrors, GetItemDocumentTypeSearchData, GetItemDocumentTypeSearchResponses, GetItemDocumentTypeSearchErrors, GetTreeDocumentTypeAncestorsData, GetTreeDocumentTypeAncestorsResponses, GetTreeDocumentTypeAncestorsErrors, GetTreeDocumentTypeChildrenData, GetTreeDocumentTypeChildrenResponses, GetTreeDocumentTypeChildrenErrors, GetTreeDocumentTypeRootData, GetTreeDocumentTypeRootResponses, GetTreeDocumentTypeRootErrors, GetTreeDocumentTypeSiblingsData, GetTreeDocumentTypeSiblingsResponses, GetTreeDocumentTypeSiblingsErrors, GetDocumentVersionData, GetDocumentVersionResponses, GetDocumentVersionErrors, GetDocumentVersionByIdData, GetDocumentVersionByIdResponses, GetDocumentVersionByIdErrors, PutDocumentVersionByIdPreventCleanupData, PutDocumentVersionByIdPreventCleanupResponses, PutDocumentVersionByIdPreventCleanupErrors, PostDocumentVersionByIdRollbackData, PostDocumentVersionByIdRollbackResponses, PostDocumentVersionByIdRollbackErrors, GetCollectionDocumentByIdData, GetCollectionDocumentByIdResponses, GetCollectionDocumentByIdErrors, PostDocumentData, PostDocumentResponses, PostDocumentErrors, DeleteDocumentByIdData, DeleteDocumentByIdResponses, DeleteDocumentByIdErrors, GetDocumentByIdData, GetDocumentByIdResponses, GetDocumentByIdErrors, PutDocumentByIdData, PutDocumentByIdResponses, PutDocumentByIdErrors, GetDocumentByIdAuditLogData, GetDocumentByIdAuditLogResponses, GetDocumentByIdAuditLogErrors, PostDocumentByIdCopyData, PostDocumentByIdCopyResponses, PostDocumentByIdCopyErrors, GetDocumentByIdDomainsData, GetDocumentByIdDomainsResponses, GetDocumentByIdDomainsErrors, PutDocumentByIdDomainsData, PutDocumentByIdDomainsResponses, PutDocumentByIdDomainsErrors, PutDocumentByIdMoveData, PutDocumentByIdMoveResponses, PutDocumentByIdMoveErrors, PutDocumentByIdMoveToRecycleBinData, PutDocumentByIdMoveToRecycleBinResponses, PutDocumentByIdMoveToRecycleBinErrors, GetDocumentByIdNotificationsData, GetDocumentByIdNotificationsResponses, GetDocumentByIdNotificationsErrors, PutDocumentByIdNotificationsData, PutDocumentByIdNotificationsResponses, PutDocumentByIdNotificationsErrors, DeleteDocumentByIdPublicAccessData, DeleteDocumentByIdPublicAccessResponses, DeleteDocumentByIdPublicAccessErrors, GetDocumentByIdPublicAccessData, GetDocumentByIdPublicAccessResponses, GetDocumentByIdPublicAccessErrors, PostDocumentByIdPublicAccessData, PostDocumentByIdPublicAccessResponses, PostDocumentByIdPublicAccessErrors, PutDocumentByIdPublicAccessData, PutDocumentByIdPublicAccessResponses, PutDocumentByIdPublicAccessErrors, PutDocumentByIdPublishData, PutDocumentByIdPublishResponses, PutDocumentByIdPublishErrors, PutDocumentByIdPublishWithDescendantsData, PutDocumentByIdPublishWithDescendantsResponses, PutDocumentByIdPublishWithDescendantsErrors, GetDocumentByIdPublishWithDescendantsResultByTaskIdData, GetDocumentByIdPublishWithDescendantsResultByTaskIdResponses, GetDocumentByIdPublishWithDescendantsResultByTaskIdErrors, GetDocumentByIdPublishedData, GetDocumentByIdPublishedResponses, GetDocumentByIdPublishedErrors, GetDocumentByIdReferencedByData, GetDocumentByIdReferencedByResponses, GetDocumentByIdReferencedByErrors, GetDocumentByIdReferencedDescendantsData, GetDocumentByIdReferencedDescendantsResponses, GetDocumentByIdReferencedDescendantsErrors, PutDocumentByIdUnpublishData, PutDocumentByIdUnpublishResponses, PutDocumentByIdUnpublishErrors, PutUmbracoManagementApiV11DocumentByIdValidate11Data, PutUmbracoManagementApiV11DocumentByIdValidate11Responses, PutUmbracoManagementApiV11DocumentByIdValidate11Errors, GetDocumentAreReferencedData, GetDocumentAreReferencedResponses, GetDocumentAreReferencedErrors, GetDocumentConfigurationData, GetDocumentConfigurationResponses, GetDocumentConfigurationErrors, PutDocumentSortData, PutDocumentSortResponses, PutDocumentSortErrors, GetDocumentUrlsData, GetDocumentUrlsResponses, GetDocumentUrlsErrors, PostDocumentValidateData, PostDocumentValidateResponses, PostDocumentValidateErrors, GetItemDocumentData, GetItemDocumentResponses, GetItemDocumentErrors, GetItemDocumentSearchData, GetItemDocumentSearchResponses, GetItemDocumentSearchErrors, DeleteRecycleBinDocumentData, DeleteRecycleBinDocumentResponses, DeleteRecycleBinDocumentErrors, DeleteRecycleBinDocumentByIdData, DeleteRecycleBinDocumentByIdResponses, DeleteRecycleBinDocumentByIdErrors, GetRecycleBinDocumentByIdOriginalParentData, GetRecycleBinDocumentByIdOriginalParentResponses, GetRecycleBinDocumentByIdOriginalParentErrors, PutRecycleBinDocumentByIdRestoreData, PutRecycleBinDocumentByIdRestoreResponses, PutRecycleBinDocumentByIdRestoreErrors, GetRecycleBinDocumentChildrenData, GetRecycleBinDocumentChildrenResponses, GetRecycleBinDocumentChildrenErrors, GetRecycleBinDocumentReferencedByData, GetRecycleBinDocumentReferencedByResponses, GetRecycleBinDocumentReferencedByErrors, GetRecycleBinDocumentRootData, GetRecycleBinDocumentRootResponses, GetRecycleBinDocumentRootErrors, GetTreeDocumentAncestorsData, GetTreeDocumentAncestorsResponses, GetTreeDocumentAncestorsErrors, GetTreeDocumentChildrenData, GetTreeDocumentChildrenResponses, GetTreeDocumentChildrenErrors, GetTreeDocumentRootData, GetTreeDocumentRootResponses, GetTreeDocumentRootErrors, GetTreeDocumentSiblingsData, GetTreeDocumentSiblingsResponses, GetTreeDocumentSiblingsErrors, PostDynamicRootQueryData, PostDynamicRootQueryResponses, PostDynamicRootQueryErrors, GetDynamicRootStepsData, GetDynamicRootStepsResponses, GetDynamicRootStepsErrors, GetHealthCheckGroupData, GetHealthCheckGroupResponses, GetHealthCheckGroupErrors, GetHealthCheckGroupByNameData, GetHealthCheckGroupByNameResponses, GetHealthCheckGroupByNameErrors, PostHealthCheckGroupByNameCheckData, PostHealthCheckGroupByNameCheckResponses, PostHealthCheckGroupByNameCheckErrors, PostHealthCheckExecuteActionData, PostHealthCheckExecuteActionResponses, PostHealthCheckExecuteActionErrors, GetHelpData, GetHelpResponses, GetHelpErrors, GetImagingResizeUrlsData, GetImagingResizeUrlsResponses, GetImagingResizeUrlsErrors, GetImportAnalyzeData, GetImportAnalyzeResponses, GetImportAnalyzeErrors, GetIndexerData, GetIndexerResponses, GetIndexerErrors, GetIndexerByIndexNameData, GetIndexerByIndexNameResponses, GetIndexerByIndexNameErrors, PostIndexerByIndexNameRebuildData, PostIndexerByIndexNameRebuildResponses, PostIndexerByIndexNameRebuildErrors, GetInstallSettingsData, GetInstallSettingsResponses, GetInstallSettingsErrors, PostInstallSetupData, PostInstallSetupResponses, PostInstallSetupErrors, PostInstallValidateDatabaseData, PostInstallValidateDatabaseResponses, PostInstallValidateDatabaseErrors, GetItemLanguageData, GetItemLanguageResponses, GetItemLanguageErrors, GetItemLanguageDefaultData, GetItemLanguageDefaultResponses, GetItemLanguageDefaultErrors, GetLanguageData, GetLanguageResponses, GetLanguageErrors, PostLanguageData, PostLanguageResponses, PostLanguageErrors, DeleteLanguageByIsoCodeData, DeleteLanguageByIsoCodeResponses, DeleteLanguageByIsoCodeErrors, GetLanguageByIsoCodeData, GetLanguageByIsoCodeResponses, GetLanguageByIsoCodeErrors, PutLanguageByIsoCodeData, PutLanguageByIsoCodeResponses, PutLanguageByIsoCodeErrors, GetLogViewerLevelData, GetLogViewerLevelResponses, GetLogViewerLevelErrors, GetLogViewerLevelCountData, GetLogViewerLevelCountResponses, GetLogViewerLevelCountErrors, GetLogViewerLogData, GetLogViewerLogResponses, GetLogViewerLogErrors, GetLogViewerMessageTemplateData, GetLogViewerMessageTemplateResponses, GetLogViewerMessageTemplateErrors, GetLogViewerSavedSearchData, GetLogViewerSavedSearchResponses, GetLogViewerSavedSearchErrors, PostLogViewerSavedSearchData, PostLogViewerSavedSearchResponses, PostLogViewerSavedSearchErrors, DeleteLogViewerSavedSearchByNameData, DeleteLogViewerSavedSearchByNameResponses, DeleteLogViewerSavedSearchByNameErrors, GetLogViewerSavedSearchByNameData, GetLogViewerSavedSearchByNameResponses, GetLogViewerSavedSearchByNameErrors, GetLogViewerValidateLogsSizeData, GetLogViewerValidateLogsSizeResponses, GetLogViewerValidateLogsSizeErrors, GetManifestManifestData, GetManifestManifestResponses, GetManifestManifestErrors, GetManifestManifestPrivateData, GetManifestManifestPrivateResponses, GetManifestManifestPrivateErrors, GetManifestManifestPublicData, GetManifestManifestPublicResponses, GetItemMediaTypeData, GetItemMediaTypeResponses, GetItemMediaTypeErrors, GetItemMediaTypeAllowedData, GetItemMediaTypeAllowedResponses, GetItemMediaTypeAllowedErrors, GetItemMediaTypeFoldersData, GetItemMediaTypeFoldersResponses, GetItemMediaTypeFoldersErrors, GetItemMediaTypeSearchData, GetItemMediaTypeSearchResponses, GetItemMediaTypeSearchErrors, PostMediaTypeData, PostMediaTypeResponses, PostMediaTypeErrors, DeleteMediaTypeByIdData, DeleteMediaTypeByIdResponses, DeleteMediaTypeByIdErrors, GetMediaTypeByIdData, GetMediaTypeByIdResponses, GetMediaTypeByIdErrors, PutMediaTypeByIdData, PutMediaTypeByIdResponses, PutMediaTypeByIdErrors, GetMediaTypeByIdAllowedChildrenData, GetMediaTypeByIdAllowedChildrenResponses, GetMediaTypeByIdAllowedChildrenErrors, GetMediaTypeByIdCompositionReferencesData, GetMediaTypeByIdCompositionReferencesResponses, GetMediaTypeByIdCompositionReferencesErrors, PostMediaTypeByIdCopyData, PostMediaTypeByIdCopyResponses, PostMediaTypeByIdCopyErrors, GetMediaTypeByIdExportData, GetMediaTypeByIdExportResponses, GetMediaTypeByIdExportErrors, PutMediaTypeByIdImportData, PutMediaTypeByIdImportResponses, PutMediaTypeByIdImportErrors, PutMediaTypeByIdMoveData, PutMediaTypeByIdMoveResponses, PutMediaTypeByIdMoveErrors, GetMediaTypeAllowedAtRootData, GetMediaTypeAllowedAtRootResponses, GetMediaTypeAllowedAtRootErrors, PostMediaTypeAvailableCompositionsData, PostMediaTypeAvailableCompositionsResponses, PostMediaTypeAvailableCompositionsErrors, GetMediaTypeConfigurationData, GetMediaTypeConfigurationResponses, GetMediaTypeConfigurationErrors, PostMediaTypeFolderData, PostMediaTypeFolderResponses, PostMediaTypeFolderErrors, DeleteMediaTypeFolderByIdData, DeleteMediaTypeFolderByIdResponses, DeleteMediaTypeFolderByIdErrors, GetMediaTypeFolderByIdData, GetMediaTypeFolderByIdResponses, GetMediaTypeFolderByIdErrors, PutMediaTypeFolderByIdData, PutMediaTypeFolderByIdResponses, PutMediaTypeFolderByIdErrors, PostMediaTypeImportData, PostMediaTypeImportResponses, PostMediaTypeImportErrors, GetTreeMediaTypeAncestorsData, GetTreeMediaTypeAncestorsResponses, GetTreeMediaTypeAncestorsErrors, GetTreeMediaTypeChildrenData, GetTreeMediaTypeChildrenResponses, GetTreeMediaTypeChildrenErrors, GetTreeMediaTypeRootData, GetTreeMediaTypeRootResponses, GetTreeMediaTypeRootErrors, GetTreeMediaTypeSiblingsData, GetTreeMediaTypeSiblingsResponses, GetTreeMediaTypeSiblingsErrors, GetCollectionMediaData, GetCollectionMediaResponses, GetCollectionMediaErrors, GetItemMediaData, GetItemMediaResponses, GetItemMediaErrors, GetItemMediaSearchData, GetItemMediaSearchResponses, GetItemMediaSearchErrors, PostMediaData, PostMediaResponses, PostMediaErrors, DeleteMediaByIdData, DeleteMediaByIdResponses, DeleteMediaByIdErrors, GetMediaByIdData, GetMediaByIdResponses, GetMediaByIdErrors, PutMediaByIdData, PutMediaByIdResponses, PutMediaByIdErrors, GetMediaByIdAuditLogData, GetMediaByIdAuditLogResponses, GetMediaByIdAuditLogErrors, PutMediaByIdMoveData, PutMediaByIdMoveResponses, PutMediaByIdMoveErrors, PutMediaByIdMoveToRecycleBinData, PutMediaByIdMoveToRecycleBinResponses, PutMediaByIdMoveToRecycleBinErrors, GetMediaByIdReferencedByData, GetMediaByIdReferencedByResponses, GetMediaByIdReferencedByErrors, GetMediaByIdReferencedDescendantsData, GetMediaByIdReferencedDescendantsResponses, GetMediaByIdReferencedDescendantsErrors, PutMediaByIdValidateData, PutMediaByIdValidateResponses, PutMediaByIdValidateErrors, GetMediaAreReferencedData, GetMediaAreReferencedResponses, GetMediaAreReferencedErrors, GetMediaConfigurationData, GetMediaConfigurationResponses, GetMediaConfigurationErrors, PutMediaSortData, PutMediaSortResponses, PutMediaSortErrors, GetMediaUrlsData, GetMediaUrlsResponses, GetMediaUrlsErrors, PostMediaValidateData, PostMediaValidateResponses, PostMediaValidateErrors, DeleteRecycleBinMediaData, DeleteRecycleBinMediaResponses, DeleteRecycleBinMediaErrors, DeleteRecycleBinMediaByIdData, DeleteRecycleBinMediaByIdResponses, DeleteRecycleBinMediaByIdErrors, GetRecycleBinMediaByIdOriginalParentData, GetRecycleBinMediaByIdOriginalParentResponses, GetRecycleBinMediaByIdOriginalParentErrors, PutRecycleBinMediaByIdRestoreData, PutRecycleBinMediaByIdRestoreResponses, PutRecycleBinMediaByIdRestoreErrors, GetRecycleBinMediaChildrenData, GetRecycleBinMediaChildrenResponses, GetRecycleBinMediaChildrenErrors, GetRecycleBinMediaReferencedByData, GetRecycleBinMediaReferencedByResponses, GetRecycleBinMediaReferencedByErrors, GetRecycleBinMediaRootData, GetRecycleBinMediaRootResponses, GetRecycleBinMediaRootErrors, GetTreeMediaAncestorsData, GetTreeMediaAncestorsResponses, GetTreeMediaAncestorsErrors, GetTreeMediaChildrenData, GetTreeMediaChildrenResponses, GetTreeMediaChildrenErrors, GetTreeMediaRootData, GetTreeMediaRootResponses, GetTreeMediaRootErrors, GetTreeMediaSiblingsData, GetTreeMediaSiblingsResponses, GetTreeMediaSiblingsErrors, GetItemMemberGroupData, GetItemMemberGroupResponses, GetItemMemberGroupErrors, GetMemberGroupData, GetMemberGroupResponses, GetMemberGroupErrors, PostMemberGroupData, PostMemberGroupResponses, PostMemberGroupErrors, DeleteMemberGroupByIdData, DeleteMemberGroupByIdResponses, DeleteMemberGroupByIdErrors, GetMemberGroupByIdData, GetMemberGroupByIdResponses, GetMemberGroupByIdErrors, PutMemberGroupByIdData, PutMemberGroupByIdResponses, PutMemberGroupByIdErrors, GetTreeMemberGroupRootData, GetTreeMemberGroupRootResponses, GetTreeMemberGroupRootErrors, GetItemMemberTypeData, GetItemMemberTypeResponses, GetItemMemberTypeErrors, GetItemMemberTypeSearchData, GetItemMemberTypeSearchResponses, GetItemMemberTypeSearchErrors, PostMemberTypeData, PostMemberTypeResponses, PostMemberTypeErrors, DeleteMemberTypeByIdData, DeleteMemberTypeByIdResponses, DeleteMemberTypeByIdErrors, GetMemberTypeByIdData, GetMemberTypeByIdResponses, GetMemberTypeByIdErrors, PutMemberTypeByIdData, PutMemberTypeByIdResponses, PutMemberTypeByIdErrors, GetMemberTypeByIdCompositionReferencesData, GetMemberTypeByIdCompositionReferencesResponses, GetMemberTypeByIdCompositionReferencesErrors, PostMemberTypeByIdCopyData, PostMemberTypeByIdCopyResponses, PostMemberTypeByIdCopyErrors, PostMemberTypeAvailableCompositionsData, PostMemberTypeAvailableCompositionsResponses, PostMemberTypeAvailableCompositionsErrors, GetMemberTypeConfigurationData, GetMemberTypeConfigurationResponses, GetMemberTypeConfigurationErrors, GetTreeMemberTypeRootData, GetTreeMemberTypeRootResponses, GetTreeMemberTypeRootErrors, GetFilterMemberData, GetFilterMemberResponses, GetFilterMemberErrors, GetItemMemberData, GetItemMemberResponses, GetItemMemberErrors, GetItemMemberSearchData, GetItemMemberSearchResponses, GetItemMemberSearchErrors, PostMemberData, PostMemberResponses, PostMemberErrors, DeleteMemberByIdData, DeleteMemberByIdResponses, DeleteMemberByIdErrors, GetMemberByIdData, GetMemberByIdResponses, GetMemberByIdErrors, PutMemberByIdData, PutMemberByIdResponses, PutMemberByIdErrors, GetMemberByIdReferencedByData, GetMemberByIdReferencedByResponses, GetMemberByIdReferencedByErrors, GetMemberByIdReferencedDescendantsData, GetMemberByIdReferencedDescendantsResponses, GetMemberByIdReferencedDescendantsErrors, PutMemberByIdValidateData, PutMemberByIdValidateResponses, PutMemberByIdValidateErrors, GetMemberAreReferencedData, GetMemberAreReferencedResponses, GetMemberAreReferencedErrors, GetMemberConfigurationData, GetMemberConfigurationResponses, GetMemberConfigurationErrors, PostMemberValidateData, PostMemberValidateResponses, PostMemberValidateErrors, PostModelsBuilderBuildData, PostModelsBuilderBuildResponses, PostModelsBuilderBuildErrors, GetModelsBuilderDashboardData, GetModelsBuilderDashboardResponses, GetModelsBuilderDashboardErrors, GetModelsBuilderStatusData, GetModelsBuilderStatusResponses, GetModelsBuilderStatusErrors, GetObjectTypesData, GetObjectTypesResponses, GetObjectTypesErrors, GetOembedQueryData, GetOembedQueryResponses, GetOembedQueryErrors, PostPackageByNameRunMigrationData, PostPackageByNameRunMigrationResponses, PostPackageByNameRunMigrationErrors, GetPackageConfigurationData, GetPackageConfigurationResponses, GetPackageConfigurationErrors, GetPackageCreatedData, GetPackageCreatedResponses, GetPackageCreatedErrors, PostPackageCreatedData, PostPackageCreatedResponses, PostPackageCreatedErrors, DeletePackageCreatedByIdData, DeletePackageCreatedByIdResponses, DeletePackageCreatedByIdErrors, GetPackageCreatedByIdData, GetPackageCreatedByIdResponses, GetPackageCreatedByIdErrors, PutPackageCreatedByIdData, PutPackageCreatedByIdResponses, PutPackageCreatedByIdErrors, GetPackageCreatedByIdDownloadData, GetPackageCreatedByIdDownloadResponses, GetPackageCreatedByIdDownloadErrors, GetPackageMigrationStatusData, GetPackageMigrationStatusResponses, GetPackageMigrationStatusErrors, GetItemPartialViewData, GetItemPartialViewResponses, GetItemPartialViewErrors, PostPartialViewData, PostPartialViewResponses, PostPartialViewErrors, DeletePartialViewByPathData, DeletePartialViewByPathResponses, DeletePartialViewByPathErrors, GetPartialViewByPathData, GetPartialViewByPathResponses, GetPartialViewByPathErrors, PutPartialViewByPathData, PutPartialViewByPathResponses, PutPartialViewByPathErrors, PutPartialViewByPathRenameData, PutPartialViewByPathRenameResponses, PutPartialViewByPathRenameErrors, PostPartialViewFolderData, PostPartialViewFolderResponses, PostPartialViewFolderErrors, DeletePartialViewFolderByPathData, DeletePartialViewFolderByPathResponses, DeletePartialViewFolderByPathErrors, GetPartialViewFolderByPathData, GetPartialViewFolderByPathResponses, GetPartialViewFolderByPathErrors, GetPartialViewSnippetData, GetPartialViewSnippetResponses, GetPartialViewSnippetErrors, GetPartialViewSnippetByIdData, GetPartialViewSnippetByIdResponses, GetPartialViewSnippetByIdErrors, GetTreePartialViewAncestorsData, GetTreePartialViewAncestorsResponses, GetTreePartialViewAncestorsErrors, GetTreePartialViewChildrenData, GetTreePartialViewChildrenResponses, GetTreePartialViewChildrenErrors, GetTreePartialViewRootData, GetTreePartialViewRootResponses, GetTreePartialViewRootErrors, DeletePreviewData, DeletePreviewResponses, PostPreviewData, PostPreviewResponses, PostPreviewErrors, GetProfilingStatusData, GetProfilingStatusResponses, GetProfilingStatusErrors, PutProfilingStatusData, PutProfilingStatusResponses, PutProfilingStatusErrors, GetPropertyTypeIsUsedData, GetPropertyTypeIsUsedResponses, GetPropertyTypeIsUsedErrors, PostPublishedCacheRebuildData, PostPublishedCacheRebuildResponses, PostPublishedCacheRebuildErrors, GetPublishedCacheRebuildStatusData, GetPublishedCacheRebuildStatusResponses, GetPublishedCacheRebuildStatusErrors, PostPublishedCacheReloadData, PostPublishedCacheReloadResponses, PostPublishedCacheReloadErrors, GetRedirectManagementData, GetRedirectManagementResponses, GetRedirectManagementErrors, DeleteRedirectManagementByIdData, DeleteRedirectManagementByIdResponses, DeleteRedirectManagementByIdErrors, GetRedirectManagementByIdData, GetRedirectManagementByIdResponses, GetRedirectManagementByIdErrors, GetRedirectManagementStatusData, GetRedirectManagementStatusResponses, GetRedirectManagementStatusErrors, PostRedirectManagementStatusData, PostRedirectManagementStatusResponses, PostRedirectManagementStatusErrors, GetItemRelationTypeData, GetItemRelationTypeResponses, GetItemRelationTypeErrors, GetRelationTypeData, GetRelationTypeResponses, GetRelationTypeErrors, GetRelationTypeByIdData, GetRelationTypeByIdResponses, GetRelationTypeByIdErrors, GetRelationByRelationTypeIdData, GetRelationByRelationTypeIdResponses, GetRelationByRelationTypeIdErrors, GetItemScriptData, GetItemScriptResponses, GetItemScriptErrors, PostScriptData, PostScriptResponses, PostScriptErrors, DeleteScriptByPathData, DeleteScriptByPathResponses, DeleteScriptByPathErrors, GetScriptByPathData, GetScriptByPathResponses, GetScriptByPathErrors, PutScriptByPathData, PutScriptByPathResponses, PutScriptByPathErrors, PutScriptByPathRenameData, PutScriptByPathRenameResponses, PutScriptByPathRenameErrors, PostScriptFolderData, PostScriptFolderResponses, PostScriptFolderErrors, DeleteScriptFolderByPathData, DeleteScriptFolderByPathResponses, DeleteScriptFolderByPathErrors, GetScriptFolderByPathData, GetScriptFolderByPathResponses, GetScriptFolderByPathErrors, GetTreeScriptAncestorsData, GetTreeScriptAncestorsResponses, GetTreeScriptAncestorsErrors, GetTreeScriptChildrenData, GetTreeScriptChildrenResponses, GetTreeScriptChildrenErrors, GetTreeScriptRootData, GetTreeScriptRootResponses, GetTreeScriptRootErrors, GetSearcherData, GetSearcherResponses, GetSearcherErrors, GetSearcherBySearcherNameQueryData, GetSearcherBySearcherNameQueryResponses, GetSearcherBySearcherNameQueryErrors, GetSecurityConfigurationData, GetSecurityConfigurationResponses, GetSecurityConfigurationErrors, PostSecurityForgotPasswordData, PostSecurityForgotPasswordResponses, PostSecurityForgotPasswordErrors, PostSecurityForgotPasswordResetData, PostSecurityForgotPasswordResetResponses, PostSecurityForgotPasswordResetErrors, PostSecurityForgotPasswordVerifyData, PostSecurityForgotPasswordVerifyResponses, PostSecurityForgotPasswordVerifyErrors, GetSegmentData, GetSegmentResponses, GetSegmentErrors, GetServerConfigurationData, GetServerConfigurationResponses, GetServerInformationData, GetServerInformationResponses, GetServerInformationErrors, GetServerStatusData, GetServerStatusResponses, GetServerStatusErrors, GetServerTroubleshootingData, GetServerTroubleshootingResponses, GetServerTroubleshootingErrors, GetServerUpgradeCheckData, GetServerUpgradeCheckResponses, GetServerUpgradeCheckErrors, GetItemStaticFileData, GetItemStaticFileResponses, GetItemStaticFileErrors, GetTreeStaticFileAncestorsData, GetTreeStaticFileAncestorsResponses, GetTreeStaticFileAncestorsErrors, GetTreeStaticFileChildrenData, GetTreeStaticFileChildrenResponses, GetTreeStaticFileChildrenErrors, GetTreeStaticFileRootData, GetTreeStaticFileRootResponses, GetTreeStaticFileRootErrors, GetItemStylesheetData, GetItemStylesheetResponses, GetItemStylesheetErrors, PostStylesheetData, PostStylesheetResponses, PostStylesheetErrors, DeleteStylesheetByPathData, DeleteStylesheetByPathResponses, DeleteStylesheetByPathErrors, GetStylesheetByPathData, GetStylesheetByPathResponses, GetStylesheetByPathErrors, PutStylesheetByPathData, PutStylesheetByPathResponses, PutStylesheetByPathErrors, PutStylesheetByPathRenameData, PutStylesheetByPathRenameResponses, PutStylesheetByPathRenameErrors, PostStylesheetFolderData, PostStylesheetFolderResponses, PostStylesheetFolderErrors, DeleteStylesheetFolderByPathData, DeleteStylesheetFolderByPathResponses, DeleteStylesheetFolderByPathErrors, GetStylesheetFolderByPathData, GetStylesheetFolderByPathResponses, GetStylesheetFolderByPathErrors, GetTreeStylesheetAncestorsData, GetTreeStylesheetAncestorsResponses, GetTreeStylesheetAncestorsErrors, GetTreeStylesheetChildrenData, GetTreeStylesheetChildrenResponses, GetTreeStylesheetChildrenErrors, GetTreeStylesheetRootData, GetTreeStylesheetRootResponses, GetTreeStylesheetRootErrors, GetTagData, GetTagResponses, GetTagErrors, GetTelemetryData, GetTelemetryResponses, GetTelemetryErrors, GetTelemetryLevelData, GetTelemetryLevelResponses, GetTelemetryLevelErrors, PostTelemetryLevelData, PostTelemetryLevelResponses, PostTelemetryLevelErrors, GetItemTemplateData, GetItemTemplateResponses, GetItemTemplateErrors, GetItemTemplateSearchData, GetItemTemplateSearchResponses, GetItemTemplateSearchErrors, PostTemplateData, PostTemplateResponses, PostTemplateErrors, DeleteTemplateByIdData, DeleteTemplateByIdResponses, DeleteTemplateByIdErrors, GetTemplateByIdData, GetTemplateByIdResponses, GetTemplateByIdErrors, PutTemplateByIdData, PutTemplateByIdResponses, PutTemplateByIdErrors, GetTemplateConfigurationData, GetTemplateConfigurationResponses, GetTemplateConfigurationErrors, PostTemplateQueryExecuteData, PostTemplateQueryExecuteResponses, PostTemplateQueryExecuteErrors, GetTemplateQuerySettingsData, GetTemplateQuerySettingsResponses, GetTemplateQuerySettingsErrors, GetTreeTemplateAncestorsData, GetTreeTemplateAncestorsResponses, GetTreeTemplateAncestorsErrors, GetTreeTemplateChildrenData, GetTreeTemplateChildrenResponses, GetTreeTemplateChildrenErrors, GetTreeTemplateRootData, GetTreeTemplateRootResponses, GetTreeTemplateRootErrors, GetTreeTemplateSiblingsData, GetTreeTemplateSiblingsResponses, GetTreeTemplateSiblingsErrors, PostTemporaryFileData, PostTemporaryFileResponses, PostTemporaryFileErrors, DeleteTemporaryFileByIdData, DeleteTemporaryFileByIdResponses, DeleteTemporaryFileByIdErrors, GetTemporaryFileByIdData, GetTemporaryFileByIdResponses, GetTemporaryFileByIdErrors, GetTemporaryFileConfigurationData, GetTemporaryFileConfigurationResponses, GetTemporaryFileConfigurationErrors, PostUpgradeAuthorizeData, PostUpgradeAuthorizeResponses, PostUpgradeAuthorizeErrors, GetUpgradeSettingsData, GetUpgradeSettingsResponses, GetUpgradeSettingsErrors, GetUserDataData, GetUserDataResponses, GetUserDataErrors, PostUserDataData, PostUserDataResponses, PostUserDataErrors, PutUserDataData, PutUserDataResponses, PutUserDataErrors, GetUserDataByIdData, GetUserDataByIdResponses, GetUserDataByIdErrors, GetFilterUserGroupData, GetFilterUserGroupResponses, GetFilterUserGroupErrors, GetItemUserGroupData, GetItemUserGroupResponses, GetItemUserGroupErrors, DeleteUserGroupData, DeleteUserGroupResponses, DeleteUserGroupErrors, GetUserGroupData, GetUserGroupResponses, GetUserGroupErrors, PostUserGroupData, PostUserGroupResponses, PostUserGroupErrors, DeleteUserGroupByIdData, DeleteUserGroupByIdResponses, DeleteUserGroupByIdErrors, GetUserGroupByIdData, GetUserGroupByIdResponses, GetUserGroupByIdErrors, PutUserGroupByIdData, PutUserGroupByIdResponses, PutUserGroupByIdErrors, DeleteUserGroupByIdUsersData, DeleteUserGroupByIdUsersResponses, DeleteUserGroupByIdUsersErrors, PostUserGroupByIdUsersData, PostUserGroupByIdUsersResponses, PostUserGroupByIdUsersErrors, GetFilterUserData, GetFilterUserResponses, GetFilterUserErrors, GetItemUserData, GetItemUserResponses, GetItemUserErrors, DeleteUserData, DeleteUserResponses, DeleteUserErrors, GetUserData, GetUserResponses, GetUserErrors, PostUserData, PostUserResponses, PostUserErrors, DeleteUserByIdData, DeleteUserByIdResponses, DeleteUserByIdErrors, GetUserByIdData, GetUserByIdResponses, GetUserByIdErrors, PutUserByIdData, PutUserByIdResponses, PutUserByIdErrors, GetUserById2FaData, GetUserById2FaResponses, GetUserById2FaErrors, DeleteUserById2FaByProviderNameData, DeleteUserById2FaByProviderNameResponses, DeleteUserById2FaByProviderNameErrors, GetUserByIdCalculateStartNodesData, GetUserByIdCalculateStartNodesResponses, GetUserByIdCalculateStartNodesErrors, PostUserByIdChangePasswordData, PostUserByIdChangePasswordResponses, PostUserByIdChangePasswordErrors, GetUserByIdClientCredentialsData, GetUserByIdClientCredentialsResponses, GetUserByIdClientCredentialsErrors, PostUserByIdClientCredentialsData, PostUserByIdClientCredentialsResponses, PostUserByIdClientCredentialsErrors, DeleteUserByIdClientCredentialsByClientIdData, DeleteUserByIdClientCredentialsByClientIdResponses, DeleteUserByIdClientCredentialsByClientIdErrors, PostUserByIdResetPasswordData, PostUserByIdResetPasswordResponses, PostUserByIdResetPasswordErrors, DeleteUserAvatarByIdData, DeleteUserAvatarByIdResponses, DeleteUserAvatarByIdErrors, PostUserAvatarByIdData, PostUserAvatarByIdResponses, PostUserAvatarByIdErrors, GetUserConfigurationData, GetUserConfigurationResponses, GetUserConfigurationErrors, GetUserCurrentData, GetUserCurrentResponses, GetUserCurrentErrors, GetUserCurrent2FaData, GetUserCurrent2FaResponses, GetUserCurrent2FaErrors, DeleteUserCurrent2FaByProviderNameData, DeleteUserCurrent2FaByProviderNameResponses, DeleteUserCurrent2FaByProviderNameErrors, GetUserCurrent2FaByProviderNameData, GetUserCurrent2FaByProviderNameResponses, GetUserCurrent2FaByProviderNameErrors, PostUserCurrent2FaByProviderNameData, PostUserCurrent2FaByProviderNameResponses, PostUserCurrent2FaByProviderNameErrors, PostUserCurrentAvatarData, PostUserCurrentAvatarResponses, PostUserCurrentAvatarErrors, PostUserCurrentChangePasswordData, PostUserCurrentChangePasswordResponses, PostUserCurrentChangePasswordErrors, GetUserCurrentConfigurationData, GetUserCurrentConfigurationResponses, GetUserCurrentConfigurationErrors, GetUserCurrentLoginProvidersData, GetUserCurrentLoginProvidersResponses, GetUserCurrentLoginProvidersErrors, GetUserCurrentPermissionsData, GetUserCurrentPermissionsResponses, GetUserCurrentPermissionsErrors, GetUserCurrentPermissionsDocumentData, GetUserCurrentPermissionsDocumentResponses, GetUserCurrentPermissionsDocumentErrors, GetUserCurrentPermissionsMediaData, GetUserCurrentPermissionsMediaResponses, GetUserCurrentPermissionsMediaErrors, PostUserDisableData, PostUserDisableResponses, PostUserDisableErrors, PostUserEnableData, PostUserEnableResponses, PostUserEnableErrors, PostUserInviteData, PostUserInviteResponses, PostUserInviteErrors, PostUserInviteCreatePasswordData, PostUserInviteCreatePasswordResponses, PostUserInviteCreatePasswordErrors, PostUserInviteResendData, PostUserInviteResendResponses, PostUserInviteResendErrors, PostUserInviteVerifyData, PostUserInviteVerifyResponses, PostUserInviteVerifyErrors, PostUserSetUserGroupsData, PostUserSetUserGroupsResponses, PostUserSetUserGroupsErrors, PostUserUnlockData, PostUserUnlockResponses, PostUserUnlockErrors, GetItemWebhookData, GetItemWebhookResponses, GetItemWebhookErrors, GetWebhookData, GetWebhookResponses, GetWebhookErrors, PostWebhookData, PostWebhookResponses, PostWebhookErrors, DeleteWebhookByIdData, DeleteWebhookByIdResponses, DeleteWebhookByIdErrors, GetWebhookByIdData, GetWebhookByIdResponses, GetWebhookByIdErrors, PutWebhookByIdData, PutWebhookByIdResponses, PutWebhookByIdErrors, GetWebhookByIdLogsData, GetWebhookByIdLogsResponses, GetWebhookByIdLogsErrors, GetWebhookEventsData, GetWebhookEventsResponses, GetWebhookEventsErrors, GetWebhookLogsData, GetWebhookLogsResponses, GetWebhookLogsErrors } from './types.gen'; import { client as _heyApiClient } from './client.gen'; @@ -1160,6 +1160,29 @@ export class DocumentVersionService { } } +class DocumentByIdValidate1Service { + public static putUmbracoManagementApiV11DocumentByIdValidate11(options: Options) { + return (options.client ?? _heyApiClient).put({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/umbraco/management/api/v1.1/document/{id}/validate', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +class PutUmbracoManagementApiV1Service { + static documentByIdValidate1Service = DocumentByIdValidate1Service; +} + export class DocumentService { public static getCollectionDocumentById(options: Options) { return (options.client ?? _heyApiClient).get({ @@ -1517,23 +1540,6 @@ export class DocumentService { }); } - public static putUmbracoManagementApiV11DocumentByIdValidate11(options: Options) { - return (options.client ?? _heyApiClient).put({ - security: [ - { - scheme: 'bearer', - type: 'http' - } - ], - url: '/umbraco/management/api/v1.1/document/{id}/validate', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options.headers - } - }); - } - public static getDocumentAreReferenced(options?: Options) { return (options?.client ?? _heyApiClient).get({ security: [ @@ -1779,6 +1785,7 @@ export class DocumentService { ...options }); } + static putUmbracoManagementApiV1Service = PutUmbracoManagementApiV1Service; } export class DynamicRootService { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/types.gen.ts b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/types.gen.ts index 15d6395949..115dd08c87 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/types.gen.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/backend-api/types.gen.ts @@ -1124,14 +1124,8 @@ export type IndexResponseModel = { } | null; }; -export type InstallRequestModelReadable = { - user: UserInstallRequestModelReadable; - database: DatabaseInstallRequestModel; - telemetryLevel: TelemetryLevelModel; -}; - -export type InstallRequestModelWritable = { - user: UserInstallRequestModelWritable; +export type InstallRequestModel = { + user: UserInstallRequestModel; database: DatabaseInstallRequestModel; telemetryLevel: TelemetryLevelModel; }; @@ -1707,7 +1701,7 @@ export type PackageConfigurationResponseModel = { marketplaceUrl: string; }; -export type PackageDefinitionResponseModelReadable = { +export type PackageDefinitionResponseModel = { name: string; contentNodeId?: string | null; contentLoadChildNodes: boolean; @@ -1726,24 +1720,6 @@ export type PackageDefinitionResponseModelReadable = { readonly packagePath: string; }; -export type PackageDefinitionResponseModelWritable = { - name: string; - contentNodeId?: string | null; - contentLoadChildNodes: boolean; - mediaIds: Array; - mediaLoadChildNodes: boolean; - documentTypes: Array; - mediaTypes: Array; - dataTypes: Array; - templates: Array; - partialViews: Array; - stylesheets: Array; - scripts: Array; - languages: Array; - dictionaryItems: Array; - id: string; -}; - export type PackageMigrationStatusResponseModel = { packageName: string; hasPendingMigrations: boolean; @@ -1949,14 +1925,9 @@ export type PagedObjectTypeResponseModel = { items: Array; }; -export type PagedPackageDefinitionResponseModelReadable = { +export type PagedPackageDefinitionResponseModel = { total: number; - items: Array; -}; - -export type PagedPackageDefinitionResponseModelWritable = { - total: number; - items: Array; + items: Array; }; export type PagedPackageMigrationStatusResponseModel = { @@ -1984,14 +1955,9 @@ export type PagedReferenceByIdModel = { items: Array; }; -export type PagedRelationResponseModelReadable = { +export type PagedRelationResponseModel = { total: number; - items: Array; -}; - -export type PagedRelationResponseModelWritable = { - total: number; - items: Array; + items: Array; }; export type PagedRelationTypeResponseModel = { @@ -2004,14 +1970,9 @@ export type PagedSavedLogSearchResponseModel = { items: Array; }; -export type PagedSearchResultResponseModelReadable = { +export type PagedSearchResultResponseModel = { total: number; - items: Array; -}; - -export type PagedSearchResultResponseModelWritable = { - total: number; - items: Array; + items: Array; }; export type PagedSearcherResponseModel = { @@ -2209,7 +2170,7 @@ export type RelationReferenceModel = { name?: string | null; }; -export type RelationResponseModelReadable = { +export type RelationResponseModel = { id: string; relationType: ReferenceByIdModel; parent: RelationReferenceModel; @@ -2218,13 +2179,6 @@ export type RelationResponseModelReadable = { readonly comment?: string | null; }; -export type RelationResponseModelWritable = { - id: string; - relationType: ReferenceByIdModel; - parent: RelationReferenceModel; - child: RelationReferenceModel; -}; - export type RelationTypeItemResponseModel = { id: string; signs: Array; @@ -2323,19 +2277,13 @@ export type ScriptResponseModel = { content: string; }; -export type SearchResultResponseModelReadable = { +export type SearchResultResponseModel = { id: string; score: number; readonly fieldCount: number; fields: Array; }; -export type SearchResultResponseModelWritable = { - id: string; - score: number; - fields: Array; -}; - export type SearcherResponseModel = { name: string; }; @@ -2788,7 +2736,7 @@ export type UpdateMemberTypeRequestModel = { compositions: Array; }; -export type UpdatePackageRequestModelReadable = { +export type UpdatePackageRequestModel = { name: string; contentNodeId?: string | null; contentLoadChildNodes: boolean; @@ -2806,23 +2754,6 @@ export type UpdatePackageRequestModelReadable = { readonly packagePath: string; }; -export type UpdatePackageRequestModelWritable = { - name: string; - contentNodeId?: string | null; - contentLoadChildNodes: boolean; - mediaIds: Array; - mediaLoadChildNodes: boolean; - documentTypes: Array; - mediaTypes: Array; - dataTypes: Array; - templates: Array; - partialViews: Array; - stylesheets: Array; - scripts: Array; - languages: Array; - dictionaryItems: Array; -}; - export type UpdatePartialViewRequestModel = { content: string; }; @@ -2898,7 +2829,7 @@ export type UpgradeCheckResponseModel = { url: string; }; -export type UpgradeSettingsResponseModelReadable = { +export type UpgradeSettingsResponseModel = { currentState: string; newState: string; newVersion: string; @@ -2906,13 +2837,6 @@ export type UpgradeSettingsResponseModelReadable = { readonly reportUrl: string; }; -export type UpgradeSettingsResponseModelWritable = { - currentState: string; - newState: string; - newVersion: string; - oldVersion: string; -}; - export type UserConfigurationResponseModel = { canInviteUsers: boolean; usernameIsEmail: boolean; @@ -2974,19 +2898,13 @@ export type UserGroupResponseModel = { aliasCanBeChanged: boolean; }; -export type UserInstallRequestModelReadable = { +export type UserInstallRequestModel = { name: string; email: string; password: string; readonly subscribeToNewsletter: boolean; }; -export type UserInstallRequestModelWritable = { - name: string; - email: string; - password: string; -}; - export type UserItemResponseModel = { id: string; signs: Array; @@ -7681,7 +7599,7 @@ export type GetInstallSettingsResponses = { export type GetInstallSettingsResponse = GetInstallSettingsResponses[keyof GetInstallSettingsResponses]; export type PostInstallSetupData = { - body?: InstallRequestModelWritable; + body?: InstallRequestModel; path?: never; query?: never; url: '/umbraco/management/api/v1/install/setup'; @@ -11375,7 +11293,7 @@ export type GetPackageCreatedResponses = { /** * OK */ - 200: PagedPackageDefinitionResponseModelReadable; + 200: PagedPackageDefinitionResponseModel; }; export type GetPackageCreatedResponse = GetPackageCreatedResponses[keyof GetPackageCreatedResponses]; @@ -11478,13 +11396,13 @@ export type GetPackageCreatedByIdResponses = { /** * OK */ - 200: PackageDefinitionResponseModelReadable; + 200: PackageDefinitionResponseModel; }; export type GetPackageCreatedByIdResponse = GetPackageCreatedByIdResponses[keyof GetPackageCreatedByIdResponses]; export type PutPackageCreatedByIdData = { - body?: UpdatePackageRequestModelWritable; + body?: UpdatePackageRequestModel; path: { id: string; }; @@ -12510,7 +12428,7 @@ export type GetRelationByRelationTypeIdResponses = { /** * OK */ - 200: PagedRelationResponseModelReadable; + 200: PagedRelationResponseModel; }; export type GetRelationByRelationTypeIdResponse = GetRelationByRelationTypeIdResponses[keyof GetRelationByRelationTypeIdResponses]; @@ -12974,7 +12892,7 @@ export type GetSearcherBySearcherNameQueryResponses = { /** * OK */ - 200: PagedSearchResultResponseModelReadable; + 200: PagedSearchResultResponseModel; }; export type GetSearcherBySearcherNameQueryResponse = GetSearcherBySearcherNameQueryResponses[keyof GetSearcherBySearcherNameQueryResponses]; @@ -14454,7 +14372,7 @@ export type GetUpgradeSettingsResponses = { /** * OK */ - 200: UpgradeSettingsResponseModelReadable; + 200: UpgradeSettingsResponseModel; }; export type GetUpgradeSettingsResponse = GetUpgradeSettingsResponses[keyof GetUpgradeSettingsResponses]; 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 069e41f5db..28cdbc9897 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 @@ -88,6 +88,18 @@ export class UmbExtensionSlotElement extends UmbLitElement { } #props?: Record = {}; + @property({ type: Object, attribute: false }) + set events(newVal: Record void> | undefined) { + this.#events = newVal; + if (this.#extensionsController) { + this.#addEventListenersToExtensionElement(); + } + } + get events(): Record void> | undefined { + return this.#events; + } + #events?: Record void> = {}; + @property({ type: String, attribute: 'default-element' }) public defaultElement?: string; @@ -97,6 +109,15 @@ export class UmbExtensionSlotElement extends UmbLitElement { index: number, ) => TemplateResult | TemplateResult<1> | HTMLElement | null | undefined | typeof nothing; + @property({ attribute: false }) + public fallbackRenderMethod?: () => + | TemplateResult + | TemplateResult<1> + | HTMLElement + | null + | undefined + | typeof nothing; + override connectedCallback(): void { super.connectedCallback(); this.#attached = true; @@ -104,6 +125,7 @@ export class UmbExtensionSlotElement extends UmbLitElement { } override disconnectedCallback(): void { // _permitted is reset as the extensionsController fires a callback on destroy. + this.#removeEventListenersFromExtensionElement(); this.#attached = false; this.#extensionsController?.destroy(); this.#extensionsController = undefined; @@ -121,6 +143,7 @@ export class UmbExtensionSlotElement extends UmbLitElement { this.filter, (extensionControllers) => { this._permitted = extensionControllers; + this.#addEventListenersToExtensionElement(); }, undefined, // We can leave the alias undefined as we destroy this our selfs. this.defaultElement, @@ -133,17 +156,52 @@ export class UmbExtensionSlotElement extends UmbLitElement { } override render() { + // First renders something once _permitted is set, this is to avoid flickering. [NL] return this._permitted ? this._permitted.length > 0 ? repeat(this._permitted, (ext) => ext.alias, this.#renderExtension) - : html`` + : this.#renderNoting() : nothing; } + #renderNoting() { + return this.fallbackRenderMethod ? this.fallbackRenderMethod() : html``; + } + #renderExtension = (ext: UmbExtensionElementInitializer, i: number) => { return this.renderMethod ? this.renderMethod(ext, i) : ext.component; }; + #addEventListenersToExtensionElement() { + this._permitted?.forEach((initializer) => { + const component = initializer.component as HTMLElement; + if (!component) return; + + const events = this.#events; + if (!events) return; + + this.#removeEventListenersFromExtensionElement(); + + Object.entries(events).forEach(([eventName, handler]) => { + component.addEventListener(eventName, handler); + }); + }); + } + + #removeEventListenersFromExtensionElement() { + this._permitted?.forEach((initializer) => { + const component = initializer.component as HTMLElement; + if (!component) return; + + const events = this.#events; + if (!events) return; + + Object.entries(events).forEach(([eventName, handler]) => { + component.removeEventListener(eventName, handler); + }); + }); + } + static override styles = css` :host { display: contents; 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 9b299c1b9c..9f2e56413f 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 @@ -143,6 +143,15 @@ export class UmbExtensionWithApiSlotElement extends UmbLitElement { index: number, ) => TemplateResult | TemplateResult<1> | HTMLElement | null | undefined | typeof nothing; + @property({ attribute: false }) + public fallbackRenderMethod?: () => + | TemplateResult + | TemplateResult<1> + | HTMLElement + | null + | undefined + | typeof nothing; + override connectedCallback(): void { super.connectedCallback(); this.#attached = true; @@ -185,10 +194,14 @@ export class UmbExtensionWithApiSlotElement extends UmbLitElement { return this._permitted ? this._permitted.length > 0 ? repeat(this._permitted, (ext) => ext.alias, this.#renderExtension) - : html`` + : this.#renderNoting() : nothing; } + #renderNoting() { + return this.fallbackRenderMethod ? this.fallbackRenderMethod() : html``; + } + #renderExtension = (ext: UmbExtensionElementAndApiInitializer, i: number) => { return this.renderMethod ? this.renderMethod(ext, i) : ext.component; }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/index.ts index c73d5980e7..77dbe4fc3e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/index.ts @@ -2,6 +2,8 @@ export * from './conditions/index.js'; export * from './initializers/index.js'; export * from './registry.js'; export * from './utils/index.js'; +export * from './components/index.js'; + export type * from './models/types.js'; export type * from './extensions/types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/hint/context/hints.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/hint/context/hints.context.ts index 87025e2717..3a9875d47c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/hint/context/hints.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/hint/context/hints.context.ts @@ -1,6 +1,6 @@ import type { UmbHint, UmbIncomingHintBase } from '../types.js'; import { UMB_HINT_CONTEXT } from './hint.context-token.js'; -import { UmbHintController } from './hints.controller.js'; +import { UmbHintController, type UmbHintControllerArgs } from './hints.controller.js'; import type { UmbPartialSome } from '@umbraco-cms/backoffice/utils'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -8,8 +8,8 @@ export class UmbHintContext< HintType extends UmbHint = UmbHint, IncomingHintType extends UmbIncomingHintBase = UmbPartialSome, > extends UmbHintController { - constructor(host: UmbControllerHost) { - super(host); + constructor(host: UmbControllerHost, args?: UmbHintControllerArgs) { + super(host, args); this.provideContext(UMB_HINT_CONTEXT, this as unknown as UmbHintContext); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/hint/context/hints.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/hint/context/hints.controller.ts index d655c10ae3..6b9f3ba1e6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/hint/context/hints.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/hint/context/hints.controller.ts @@ -6,6 +6,11 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbArrayState, UmbObjectState, type Observable } from '@umbraco-cms/backoffice/observable-api'; import type { UmbContextProviderController } from '@umbraco-cms/backoffice/context-api'; +export interface UmbHintControllerArgs { + viewAlias?: string; + scaffold?: Partial; +} + export class UmbHintController< HintType extends UmbHint = UmbHint, IncomingHintType extends UmbIncomingHintBase = UmbPartialSome, @@ -35,7 +40,7 @@ export class UmbHintController< return this.#scaffold.getValue(); } - constructor(host: UmbControllerHost, args?: { viewAlias?: string; scaffold?: Partial }) { + constructor(host: UmbControllerHost, args?: UmbHintControllerArgs) { super(host); this.#viewAlias = args?.viewAlias; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/http-client/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/http-client/index.ts index 8b39688895..450649ecb2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/http-client/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/http-client/index.ts @@ -1,2 +1 @@ export { client as umbHttpClient } from '@umbraco-cms/backoffice/external/backend-api'; -export type { RequestOptions } from '@hey-api/client-fetch'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts index 353fe7c641..653bf6a54e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons.ts @@ -1,3260 +1,2554 @@ -export default [ - { - name: 'icon-activity', - path: () => import('./icons/icon-activity.js'), - }, - { - name: 'icon-add', - path: () => import('./icons/icon-add.js'), - }, - { - name: 'icon-addressbook', - path: () => import('./icons/icon-addressbook.js'), - }, - { - name: 'icon-alarm-clock', - path: () => import('./icons/icon-alarm-clock.js'), - }, - { - name: 'icon-alert-alt', - path: () => import('./icons/icon-alert-alt.js'), - }, - { - name: 'icon-alert', - path: () => import('./icons/icon-alert.js'), - }, - { - name: 'icon-alt', - path: () => import('./icons/icon-alt.js'), - }, - { - name: 'icon-anchor', - path: () => import('./icons/icon-anchor.js'), - }, - { - name: 'icon-app', - path: () => import('./icons/icon-app.js'), - }, - { - name: 'icon-application-error', - path: () => import('./icons/icon-application-error.js'), - }, - { - name: 'icon-application-window-alt', - path: () => import('./icons/icon-application-window-alt.js'), - }, - { - name: 'icon-application-window', - path: () => import('./icons/icon-application-window.js'), - }, - { - name: 'icon-arrivals', - path: () => import('./icons/icon-arrivals.js'), - }, - { - name: 'icon-arrow-down', - path: () => import('./icons/icon-arrow-down.js'), - }, - { - name: 'icon-arrow-left', - path: () => import('./icons/icon-arrow-left.js'), - }, - { - name: 'icon-arrow-right', - path: () => import('./icons/icon-arrow-right.js'), - }, - { - name: 'icon-arrow-up', - path: () => import('./icons/icon-arrow-up.js'), - }, - { - name: 'icon-attachment', - path: () => import('./icons/icon-attachment.js'), - }, - { - name: 'icon-audio-lines', - path: () => import('./icons/icon-audio-lines.js'), - }, - { - name: 'icon-autofill', - path: () => import('./icons/icon-autofill.js'), - }, - { - name: 'icon-award', - path: () => import('./icons/icon-award.js'), - }, - { - name: 'icon-axis-rotation-2', - path: () => import('./icons/icon-axis-rotation-2.js'), - }, - { - name: 'icon-axis-rotation-3', - path: () => import('./icons/icon-axis-rotation-3.js'), - }, - { - name: 'icon-axis-rotation', - path: () => import('./icons/icon-axis-rotation.js'), - }, - { - name: 'icon-backspace', - path: () => import('./icons/icon-backspace.js'), - }, - { - name: 'icon-badge-add', - path: () => import('./icons/icon-badge-add.js'), - }, - { - name: 'icon-badge-remove', - path: () => import('./icons/icon-badge-remove.js'), - }, - { - name: 'icon-badge-restricted', - legacy: true, - hidden: true, - path: () => import('./icons/icon-badge-restricted.js'), - }, - { - name: 'icon-ball', - path: () => import('./icons/icon-ball.js'), - }, - { - name: 'icon-bar-chart', - path: () => import('./icons/icon-bar-chart.js'), - }, - { - name: 'icon-barcode', - path: () => import('./icons/icon-barcode.js'), - }, - { - name: 'icon-bars', - path: () => import('./icons/icon-bars.js'), - }, - { - name: 'icon-battery-full', - path: () => import('./icons/icon-battery-full.js'), - }, - { - name: 'icon-battery-low', - path: () => import('./icons/icon-battery-low.js'), - }, - { - name: 'icon-beer-glass', - path: () => import('./icons/icon-beer-glass.js'), - }, - { - name: 'icon-bell-off', - path: () => import('./icons/icon-bell-off.js'), - }, - { - name: 'icon-bell', - path: () => import('./icons/icon-bell.js'), - }, - { - name: 'icon-binarycode', - path: () => import('./icons/icon-binarycode.js'), - }, - { - name: 'icon-binoculars', - legacy: true, - hidden: true, - path: () => import('./icons/icon-binoculars.js'), - }, - { - name: 'icon-bird', - path: () => import('./icons/icon-bird.js'), - }, - { - name: 'icon-birthday-cake', - path: () => import('./icons/icon-birthday-cake.js'), - }, - { - name: 'icon-block', - path: () => import('./icons/icon-block.js'), - }, - { - name: 'icon-blockquote', - path: () => import('./icons/icon-blockquote.js'), - }, - { - name: 'icon-bluetooth', - path: () => import('./icons/icon-bluetooth.js'), - }, - { - name: 'icon-boat-shipping', - path: () => import('./icons/icon-boat-shipping.js'), - }, - { - name: 'icon-bold', - path: () => import('./icons/icon-bold.js'), - }, - { - name: 'icon-bones', - path: () => import('./icons/icon-bones.js'), - }, - { - name: 'icon-book-alt-2', - path: () => import('./icons/icon-book-alt-2.js'), - }, - { - name: 'icon-book-alt', - path: () => import('./icons/icon-book-alt.js'), - }, - { - name: 'icon-book', - path: () => import('./icons/icon-book.js'), - }, - { - name: 'icon-bookmark', - path: () => import('./icons/icon-bookmark.js'), - }, - { - name: 'icon-books', - path: () => import('./icons/icon-books.js'), - }, - { - name: 'icon-box-alt', - path: () => import('./icons/icon-box-alt.js'), - }, - { - name: 'icon-box-open', - path: () => import('./icons/icon-box-open.js'), - }, - { - name: 'icon-box', - path: () => import('./icons/icon-box.js'), - }, - { - name: 'icon-brackets', - path: () => import('./icons/icon-brackets.js'), - }, - { - name: 'icon-brick', - path: () => import('./icons/icon-brick.js'), - }, - { - name: 'icon-briefcase', - path: () => import('./icons/icon-briefcase.js'), - }, - { - name: 'icon-browser-window', - path: () => import('./icons/icon-browser-window.js'), - }, - { - name: 'icon-brush-alt-2', - path: () => import('./icons/icon-brush-alt-2.js'), - }, - { - name: 'icon-brush-alt', - path: () => import('./icons/icon-brush-alt.js'), - }, - { - name: 'icon-brush', - path: () => import('./icons/icon-brush.js'), - }, - { - name: 'icon-bug', - path: () => import('./icons/icon-bug.js'), - }, - { - name: 'icon-bulleted-list', - path: () => import('./icons/icon-bulleted-list.js'), - }, - { - name: 'icon-burn', - path: () => import('./icons/icon-burn.js'), - }, - { - name: 'icon-bus', - path: () => import('./icons/icon-bus.js'), - }, - { - name: 'icon-calculator', - path: () => import('./icons/icon-calculator.js'), - }, - { - name: 'icon-calendar-alt', - path: () => import('./icons/icon-calendar-alt.js'), - }, - { - name: 'icon-calendar', - path: () => import('./icons/icon-calendar.js'), - }, - { - name: 'icon-camcorder', - legacy: true, - hidden: true, - path: () => import('./icons/icon-camcorder.js'), - }, - { - name: 'icon-camera-roll', - path: () => import('./icons/icon-camera-roll.js'), - }, - { - name: 'icon-candy', - path: () => import('./icons/icon-candy.js'), - }, - { - name: 'icon-caps-lock', - path: () => import('./icons/icon-caps-lock.js'), - }, - { - name: 'icon-car', - path: () => import('./icons/icon-car.js'), - }, - { - name: 'icon-categories', - path: () => import('./icons/icon-categories.js'), - }, - { - name: 'icon-certificate', - path: () => import('./icons/icon-certificate.js'), - }, - { - name: 'icon-chart-curve', - path: () => import('./icons/icon-chart-curve.js'), - }, - { - name: 'icon-chart', - path: () => import('./icons/icon-chart.js'), - }, - { - name: 'icon-chat-active', - legacy: true, - hidden: true, - path: () => import('./icons/icon-chat-active.js'), - }, - { - name: 'icon-chat', - path: () => import('./icons/icon-chat.js'), - }, - { - name: 'icon-check', - path: () => import('./icons/icon-check.js'), - }, - { - name: 'icon-checkbox-dotted', - path: () => import('./icons/icon-checkbox-dotted.js'), - }, - { - name: 'icon-checkbox-empty', - legacy: true, - hidden: true, - path: () => import('./icons/icon-checkbox-empty.js'), - }, - { - name: 'icon-checkbox', - path: () => import('./icons/icon-checkbox.js'), - }, - { - name: 'icon-chip-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-chip-alt.js'), - }, - { - name: 'icon-chip', - path: () => import('./icons/icon-chip.js'), - }, - { - name: 'icon-cinema', - path: () => import('./icons/icon-cinema.js'), - }, - { - name: 'icon-circle-dotted-active', - path: () => import('./icons/icon-circle-dotted-active.js'), - }, - { - name: 'icon-circle-dotted', - path: () => import('./icons/icon-circle-dotted.js'), - }, - { - name: 'icon-circuits', - path: () => import('./icons/icon-circuits.js'), - }, - { - name: 'icon-clear-formatting', - path: () => import('./icons/icon-clear-formatting.js'), - }, - { - name: 'icon-client', - legacy: true, - hidden: true, - path: () => import('./icons/icon-client.js'), - }, - { - name: 'icon-clipboard', - path: () => import('./icons/icon-clipboard.js'), - }, - { - name: 'icon-clipboard-copy', - path: () => import('./icons/icon-clipboard-copy.js'), - }, - { - name: 'icon-clipboard-entry', - path: () => import('./icons/icon-clipboard-entry.js'), - }, - { - name: 'icon-clipboard-paste', - path: () => import('./icons/icon-clipboard-paste.js'), - }, - { - name: 'icon-cloud-drive', - path: () => import('./icons/icon-cloud-drive.js'), - }, - { - name: 'icon-cloud-upload', - path: () => import('./icons/icon-cloud-upload.js'), - }, - { - name: 'icon-cloud', - path: () => import('./icons/icon-cloud.js'), - }, - { - name: 'icon-cloudy', - path: () => import('./icons/icon-cloudy.js'), - }, - { - name: 'icon-clubs', - path: () => import('./icons/icon-clubs.js'), - }, - { - name: 'icon-cocktail', - path: () => import('./icons/icon-cocktail.js'), - }, - { - name: 'icon-code', - path: () => import('./icons/icon-code.js'), - }, - { - name: 'icon-code-xml', - path: () => import('./icons/icon-code-xml.js'), - }, - { - name: 'icon-coffee', - path: () => import('./icons/icon-coffee.js'), - }, - { - name: 'icon-coin-dollar', - path: () => import('./icons/icon-coin-dollar.js'), - }, - { - name: 'icon-coin-euro', - path: () => import('./icons/icon-coin-euro.js'), - }, - { - name: 'icon-coin-pound', - path: () => import('./icons/icon-coin-pound.js'), - }, - { - name: 'icon-coin-yen', - path: () => import('./icons/icon-coin-yen.js'), - }, - { - name: 'icon-coins-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-coins-alt.js'), - }, - { - name: 'icon-coins', - path: () => import('./icons/icon-coins.js'), - }, - { - name: 'icon-color-bucket', - path: () => import('./icons/icon-color-bucket.js'), - }, - { - name: 'icon-colorpicker', - path: () => import('./icons/icon-colorpicker.js'), - }, - { - name: 'icon-columns', - path: () => import('./icons/icon-columns.js'), - }, - { - name: 'icon-columns-2', - path: () => import('./icons/icon-columns-2.js'), - }, - { - name: 'icon-columns-3', - path: () => import('./icons/icon-columns-3.js'), - }, - { - name: 'icon-columns-4', - path: () => import('./icons/icon-columns-4.js'), - }, - { - name: 'icon-rows-2', - path: () => import('./icons/icon-rows-2.js'), - }, - { - name: 'icon-rows-3', - path: () => import('./icons/icon-rows-3.js'), - }, - { - name: 'icon-rows-4', - path: () => import('./icons/icon-rows-4.js'), - }, - { - name: 'icon-grid-2', - path: () => import('./icons/icon-grid-2.js'), - }, - { - name: 'icon-grid-3', - path: () => import('./icons/icon-grid-3.js'), - }, - { - name: 'icon-combination-lock-open', - path: () => import('./icons/icon-combination-lock-open.js'), - }, - { - name: 'icon-combination-lock', - path: () => import('./icons/icon-combination-lock.js'), - }, - { - name: 'icon-command', - path: () => import('./icons/icon-command.js'), - }, - { - name: 'icon-company', - path: () => import('./icons/icon-company.js'), - }, - { - name: 'icon-compress', - path: () => import('./icons/icon-compress.js'), - }, - { - name: 'icon-connection', - path: () => import('./icons/icon-connection.js'), - }, - { - name: 'icon-console', - path: () => import('./icons/icon-console.js'), - }, - { - name: 'icon-contrast', - path: () => import('./icons/icon-contrast.js'), - }, - { - name: 'icon-conversation-alt', - path: () => import('./icons/icon-conversation-alt.js'), - }, - { - name: 'icon-conversation', - legacy: true, - hidden: true, - path: () => import('./icons/icon-conversation.js'), - }, - { - name: 'icon-coverflow', - path: () => import('./icons/icon-coverflow.js'), - }, - { - name: 'icon-credit-card-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-credit-card-alt.js'), - }, - { - name: 'icon-credit-card', - path: () => import('./icons/icon-credit-card.js'), - }, - { - name: 'icon-crop', - path: () => import('./icons/icon-crop.js'), - }, - { - name: 'icon-crosshair', - path: () => import('./icons/icon-crosshair.js'), - }, - { - name: 'icon-crown-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-crown-alt.js'), - }, - { - name: 'icon-crown', - path: () => import('./icons/icon-crown.js'), - }, - { - name: 'icon-cupcake', - legacy: true, - hidden: true, - path: () => import('./icons/icon-cupcake.js'), - }, - { - name: 'icon-curve', - path: () => import('./icons/icon-curve.js'), - }, - { - name: 'icon-cut', - path: () => import('./icons/icon-cut.js'), - }, - { - name: 'icon-dashboard', - path: () => import('./icons/icon-dashboard.js'), - }, - { - name: 'icon-defrag', - path: () => import('./icons/icon-defrag.js'), - }, - { - name: 'icon-delete-key', - path: () => import('./icons/icon-delete-key.js'), - }, - { - name: 'icon-delete', - path: () => import('./icons/icon-delete.js'), - }, - { - name: 'icon-departure', - path: () => import('./icons/icon-departure.js'), - }, - { - name: 'icon-desktop', - legacy: true, - hidden: true, - path: () => import('./icons/icon-desktop.js'), - }, - { - name: 'icon-diagnostics', - path: () => import('./icons/icon-diagnostics.js'), - }, - { - name: 'icon-diagonal-arrow-alt', - path: () => import('./icons/icon-diagonal-arrow-alt.js'), - }, - { - name: 'icon-diagonal-arrow', - path: () => import('./icons/icon-diagonal-arrow.js'), - }, - { - name: 'icon-diamond', - path: () => import('./icons/icon-diamond.js'), - }, - { - name: 'icon-diamonds', - path: () => import('./icons/icon-diamonds.js'), - }, - { - name: 'icon-dice', - path: () => import('./icons/icon-dice.js'), - }, - { - name: 'icon-diploma-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-diploma-alt.js'), - }, - { - name: 'icon-diploma', - path: () => import('./icons/icon-diploma.js'), - }, - { - name: 'icon-directions-alt', - path: () => import('./icons/icon-directions-alt.js'), - }, - { - name: 'icon-directions', - path: () => import('./icons/icon-directions.js'), - }, - { - name: 'icon-disc', - path: () => import('./icons/icon-disc.js'), - }, - { - name: 'icon-disk-image', - path: () => import('./icons/icon-disk-image.js'), - }, - { - name: 'icon-display', - path: () => import('./icons/icon-display.js'), - }, - { - name: 'icon-dna', - path: () => import('./icons/icon-dna.js'), - }, - { - name: 'icon-dock-connector', - path: () => import('./icons/icon-dock-connector.js'), - }, - { - name: 'icon-document-dashed-line', - legacy: true, - hidden: true, - path: () => import('./icons/icon-document-dashed-line.js'), - }, - { - name: 'icon-document', - path: () => import('./icons/icon-document.js'), - }, - { - name: 'icon-documents', - path: () => import('./icons/icon-documents.js'), - }, - { - name: 'icon-donate', - legacy: true, - hidden: true, - path: () => import('./icons/icon-donate.js'), - }, - { - name: 'icon-door-open-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-door-open-alt.js'), - }, - { - name: 'icon-door-open', - path: () => import('./icons/icon-door-open.js'), - }, - { - name: 'icon-download-alt', - path: () => import('./icons/icon-download-alt.js'), - }, - { - name: 'icon-download', - path: () => import('./icons/icon-download.js'), - }, - { - name: 'icon-drop', - path: () => import('./icons/icon-drop.js'), - }, - { - name: 'icon-eco', - path: () => import('./icons/icon-eco.js'), - }, - { - name: 'icon-economy', - legacy: true, - hidden: true, - path: () => import('./icons/icon-economy.js'), - }, - { - name: 'icon-edit', - path: () => import('./icons/icon-edit.js'), - }, - { - name: 'icon-embed', - path: () => import('./icons/icon-embed.js'), - }, - { - name: 'icon-employee', - legacy: true, - hidden: true, - path: () => import('./icons/icon-employee.js'), - }, - { - name: 'icon-energy-saving-bulb', - path: () => import('./icons/icon-energy-saving-bulb.js'), - }, - { - name: 'icon-enter', - path: () => import('./icons/icon-enter.js'), - }, - { - name: 'icon-equalizer', - path: () => import('./icons/icon-equalizer.js'), - }, - { - name: 'icon-escape', - path: () => import('./icons/icon-escape.js'), - }, - { - name: 'icon-ethernet', - path: () => import('./icons/icon-ethernet.js'), - }, - { - name: 'icon-eye', - path: () => import('./icons/icon-eye.js'), - }, - { - name: 'icon-exit-fullscreen', - path: () => import('./icons/icon-exit-fullscreen.js'), - }, - { - name: 'icon-facebook-like', - path: () => import('./icons/icon-facebook-like.js'), - }, - { - name: 'icon-factory', - path: () => import('./icons/icon-factory.js'), - }, - { - name: 'icon-favorite', - path: () => import('./icons/icon-favorite.js'), - }, - { - name: 'icon-file-cabinet', - path: () => import('./icons/icon-file-cabinet.js'), - }, - { - name: 'icon-files', - path: () => import('./icons/icon-files.js'), - }, - { - name: 'icon-filter-arrows', - path: () => import('./icons/icon-filter-arrows.js'), - }, - { - name: 'icon-filter', - path: () => import('./icons/icon-filter.js'), - }, - { - name: 'icon-fingerprint', - path: () => import('./icons/icon-fingerprint.js'), - }, - { - name: 'icon-fire', - path: () => import('./icons/icon-fire.js'), - }, - { - name: 'icon-firewire', - legacy: true, - hidden: true, - path: () => import('./icons/icon-firewire.js'), - }, - { - name: 'icon-flag-alt', - path: () => import('./icons/icon-flag-alt.js'), - }, - { - name: 'icon-flag', - path: () => import('./icons/icon-flag.js'), - }, - { - name: 'icon-flash', - path: () => import('./icons/icon-flash.js'), - }, - { - name: 'icon-flashlight', - path: () => import('./icons/icon-flashlight.js'), - }, - { - name: 'icon-flowerpot', - path: () => import('./icons/icon-flowerpot.js'), - }, - { - name: 'icon-folder', - path: () => import('./icons/icon-folder.js'), - }, - { - name: 'icon-folders', - path: () => import('./icons/icon-folders.js'), - }, - { - name: 'icon-font', - path: () => import('./icons/icon-font.js'), - }, - { - name: 'icon-food', - path: () => import('./icons/icon-food.js'), - }, - { - name: 'icon-footprints', - path: () => import('./icons/icon-footprints.js'), - }, - { - name: 'icon-forking', - path: () => import('./icons/icon-forking.js'), - }, - { - name: 'icon-frame-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-frame-alt.js'), - }, - { - name: 'icon-frame', - path: () => import('./icons/icon-frame.js'), - }, - { - name: 'icon-fullscreen-alt', - path: () => import('./icons/icon-fullscreen-alt.js'), - }, - { - name: 'icon-fullscreen', - path: () => import('./icons/icon-fullscreen.js'), - }, - { - name: 'icon-game', - path: () => import('./icons/icon-game.js'), - }, - { - name: 'icon-geometry', - legacy: true, - hidden: true, - path: () => import('./icons/icon-geometry.js'), - }, - { - name: 'icon-gift', - path: () => import('./icons/icon-gift.js'), - }, - { - name: 'icon-glasses', - path: () => import('./icons/icon-glasses.js'), - }, - { - name: 'icon-globe-alt', - path: () => import('./icons/icon-globe-alt.js'), - }, - { - name: 'icon-globe-asia', - legacy: true, - hidden: true, - path: () => import('./icons/icon-globe-asia.js'), - }, - { - name: 'icon-globe-europe-africa', - legacy: true, - hidden: true, - path: () => import('./icons/icon-globe-europe-africa.js'), - }, - { - name: 'icon-globe-inverted-america', - legacy: true, - hidden: true, - path: () => import('./icons/icon-globe-inverted-america.js'), - }, - { - name: 'icon-globe-inverted-asia', - legacy: true, - hidden: true, - path: () => import('./icons/icon-globe-inverted-asia.js'), - }, - { - name: 'icon-globe-inverted-europe-africa', - legacy: true, - hidden: true, - path: () => import('./icons/icon-globe-inverted-europe-africa.js'), - }, - { - name: 'icon-globe', - path: () => import('./icons/icon-globe.js'), - }, - { - name: 'icon-gps', - path: () => import('./icons/icon-gps.js'), - }, - { - name: 'icon-graduate', - path: () => import('./icons/icon-graduate.js'), - }, - { - name: 'icon-grid', - path: () => import('./icons/icon-grid.js'), - }, - { - name: 'icon-grip', - hidden: true, - path: () => import('./icons/icon-grip.js'), - }, - { - name: 'icon-hammer', - path: () => import('./icons/icon-hammer.js'), - }, - { - name: 'icon-hand-active-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-hand-active-alt.js'), - }, - { - name: 'icon-hand-active', - path: () => import('./icons/icon-hand-active.js'), - }, - { - name: 'icon-hand-pointer-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-hand-pointer-alt.js'), - }, - { - name: 'icon-hand-pointer', - path: () => import('./icons/icon-hand-pointer.js'), - }, - { - name: 'icon-handshake', - path: () => import('./icons/icon-handshake.js'), - }, - { - name: 'icon-handtool-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-handtool-alt.js'), - }, - { - name: 'icon-handtool', - path: () => import('./icons/icon-handtool.js'), - }, - { - name: 'icon-hard-drive-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-hard-drive-alt.js'), - }, - { - name: 'icon-hard-drive', - legacy: true, - hidden: true, - path: () => import('./icons/icon-hard-drive.js'), - }, - { - name: 'icon-heading-1', - path: () => import('./icons/icon-heading-1.js'), - }, - { - name: 'icon-heading-2', - path: () => import('./icons/icon-heading-2.js'), - }, - { - name: 'icon-heading-3', - path: () => import('./icons/icon-heading-3.js'), - }, - { - name: 'icon-heading-4', - path: () => import('./icons/icon-heading-4.js'), - }, - { - name: 'icon-headphones', - path: () => import('./icons/icon-headphones.js'), - }, - { - name: 'icon-headset', - legacy: true, - hidden: true, - path: () => import('./icons/icon-headset.js'), - }, - { - name: 'icon-hearts', - path: () => import('./icons/icon-hearts.js'), - }, - { - name: 'icon-height', - path: () => import('./icons/icon-height.js'), - }, - { - name: 'icon-help-alt', - path: () => import('./icons/icon-help-alt.js'), - }, - { - name: 'icon-help', - path: () => import('./icons/icon-help.js'), - }, - { - name: 'icon-history', - path: () => import('./icons/icon-history.js'), - }, - { - name: 'icon-home', - path: () => import('./icons/icon-home.js'), - }, - { - name: 'icon-horizontal-rule', - path: () => import('./icons/icon-horizontal-rule.js'), - }, - { - name: 'icon-hourglass', - path: () => import('./icons/icon-hourglass.js'), - }, - { - name: 'icon-imac', - legacy: true, - hidden: true, - path: () => import('./icons/icon-imac.js'), - }, - { - name: 'icon-image-up', - path: () => import('./icons/icon-image-up.js'), - }, - { - name: 'icon-inbox-full', - legacy: true, - hidden: true, - path: () => import('./icons/icon-inbox-full.js'), - }, - { - name: 'icon-inbox', - path: () => import('./icons/icon-inbox.js'), - }, - { - name: 'icon-indent', - path: () => import('./icons/icon-indent.js'), - }, - { - name: 'icon-infinity', - path: () => import('./icons/icon-infinity.js'), - }, - { - name: 'icon-info', - path: () => import('./icons/icon-info.js'), - }, - { - name: 'icon-invoice', - legacy: true, - hidden: true, - path: () => import('./icons/icon-invoice.js'), - }, - { - name: 'icon-ipad', - legacy: true, - hidden: true, - path: () => import('./icons/icon-ipad.js'), - }, - { - name: 'icon-iphone', - legacy: true, - hidden: true, - path: () => import('./icons/icon-iphone.js'), - }, - { - name: 'icon-italic', - path: () => import('./icons/icon-italic.js'), - }, - { - name: 'icon-item-arrangement', - legacy: true, - hidden: true, - path: () => import('./icons/icon-item-arrangement.js'), - }, - { - name: 'icon-junk', - path: () => import('./icons/icon-junk.js'), - }, - { - name: 'icon-key', - path: () => import('./icons/icon-key.js'), - }, - { - name: 'icon-keyboard', - path: () => import('./icons/icon-keyboard.js'), - }, - { - name: 'icon-lab', - path: () => import('./icons/icon-lab.js'), - }, - { - name: 'icon-laptop', - path: () => import('./icons/icon-laptop.js'), - }, - { - name: 'icon-layers-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-layers-alt.js'), - }, - { - name: 'icon-layers', - path: () => import('./icons/icon-layers.js'), - }, - { - name: 'icon-layout', - path: () => import('./icons/icon-layout.js'), - }, - { - name: 'icon-left-double-arrow', - path: () => import('./icons/icon-left-double-arrow.js'), - }, - { - name: 'icon-legal', - path: () => import('./icons/icon-legal.js'), - }, - { - name: 'icon-lense', - legacy: true, - hidden: true, - path: () => import('./icons/icon-lense.js'), - }, - { - name: 'icon-library', - path: () => import('./icons/icon-library.js'), - }, - { - name: 'icon-light-down', - path: () => import('./icons/icon-light-down.js'), - }, - { - name: 'icon-light-up', - path: () => import('./icons/icon-light-up.js'), - }, - { - name: 'icon-lightbulb-active', - path: () => import('./icons/icon-lightbulb-active.js'), - }, - { - name: 'icon-lightbulb', - legacy: true, - hidden: true, - path: () => import('./icons/icon-lightbulb.js'), - }, - { - name: 'icon-lightning', - path: () => import('./icons/icon-lightning.js'), - }, - { - name: 'icon-link', - path: () => import('./icons/icon-link.js'), - }, - { - name: 'icon-list', - path: () => import('./icons/icon-list.js'), - }, - { - name: 'icon-load', - legacy: true, - hidden: true, - path: () => import('./icons/icon-load.js'), - }, - { - name: 'icon-loading', - legacy: true, - hidden: true, - path: () => import('./icons/icon-loading.js'), - }, - { - name: 'icon-locate', - path: () => import('./icons/icon-locate.js'), - }, - { - name: 'icon-location-near-me', - legacy: true, - hidden: true, - path: () => import('./icons/icon-location-near-me.js'), - }, - { - name: 'icon-location-nearby', - path: () => import('./icons/icon-location-nearby.js'), - }, - { - name: 'icon-lock', - path: () => import('./icons/icon-lock.js'), - }, - { - name: 'icon-log-out', - path: () => import('./icons/icon-log-out.js'), - }, - { - name: 'icon-logout', - legacy: true, - hidden: true, - path: () => import('./icons/icon-logout.js'), - }, - { - name: 'icon-loupe', - legacy: true, - hidden: true, - path: () => import('./icons/icon-loupe.js'), - }, - { - name: 'icon-magnet', - path: () => import('./icons/icon-magnet.js'), - }, - { - name: 'icon-mailbox', - path: () => import('./icons/icon-mailbox.js'), - }, - { - name: 'icon-map-alt', - path: () => import('./icons/icon-map-alt.js'), - }, - { - name: 'icon-map-location', - legacy: true, - hidden: true, - path: () => import('./icons/icon-map-location.js'), - }, - { - name: 'icon-map-marker', - path: () => import('./icons/icon-map-marker.js'), - }, - { - name: 'icon-map', - path: () => import('./icons/icon-map.js'), - }, - { - name: 'icon-medal', - path: () => import('./icons/icon-medal.js'), - }, - { - name: 'icon-medical-emergency', - path: () => import('./icons/icon-medical-emergency.js'), - }, - { - name: 'icon-medicine', - path: () => import('./icons/icon-medicine.js'), - }, - { - name: 'icon-meeting', - legacy: true, - hidden: true, - path: () => import('./icons/icon-meeting.js'), - }, - { - name: 'icon-megaphone', - path: () => import('./icons/icon-megaphone.js'), - }, - { - name: 'icon-merge', - path: () => import('./icons/icon-merge.js'), - }, - { - name: 'icon-message-open', - path: () => import('./icons/icon-message-open.js'), - }, - { - name: 'icon-message-unopened', - legacy: true, - hidden: true, - path: () => import('./icons/icon-message-unopened.js'), - }, - { - name: 'icon-message', - path: () => import('./icons/icon-message.js'), - }, - { - name: 'icon-microscope', - path: () => import('./icons/icon-microscope.js'), - }, - { - name: 'icon-mindmap', - legacy: true, - hidden: true, - path: () => import('./icons/icon-mindmap.js'), - }, - { - name: 'icon-mobile', - path: () => import('./icons/icon-mobile.js'), - }, - { - name: 'icon-mountain', - path: () => import('./icons/icon-mountain.js'), - }, - { - name: 'icon-mouse-cursor', - path: () => import('./icons/icon-mouse-cursor.js'), - }, - { - name: 'icon-mouse', - path: () => import('./icons/icon-mouse.js'), - }, - { - name: 'icon-movie-alt', - path: () => import('./icons/icon-movie-alt.js'), - }, - { - name: 'icon-movie', - path: () => import('./icons/icon-movie.js'), - }, - { - name: 'icon-multiple-credit-cards', - path: () => import('./icons/icon-multiple-credit-cards.js'), - }, - { - name: 'icon-multiple-windows', - path: () => import('./icons/icon-multiple-windows.js'), - }, - { - name: 'icon-music', - path: () => import('./icons/icon-music.js'), - }, - { - name: 'icon-name-badge', - legacy: true, - hidden: true, - path: () => import('./icons/icon-name-badge.js'), - }, - { - name: 'icon-navigation-bottom', - legacy: true, - hidden: true, - path: () => import('./icons/icon-navigation-bottom.js'), - }, - { - name: 'icon-navigation-down', - legacy: true, - hidden: true, - path: () => import('./icons/icon-navigation-down.js'), - }, - { - name: 'icon-navigation-first', - legacy: true, - hidden: true, - path: () => import('./icons/icon-navigation-first.js'), - }, - { - name: 'icon-navigation-horizontal', - legacy: true, - hidden: true, - path: () => import('./icons/icon-navigation-horizontal.js'), - }, - { - name: 'icon-navigation-last', - legacy: true, - hidden: true, - path: () => import('./icons/icon-navigation-last.js'), - }, - { - name: 'icon-navigation-left', - legacy: true, - hidden: true, - path: () => import('./icons/icon-navigation-left.js'), - }, - { - name: 'icon-navigation-right', - legacy: true, - hidden: true, - path: () => import('./icons/icon-navigation-right.js'), - }, - { - name: 'icon-navigation-road', - legacy: true, - hidden: true, - path: () => import('./icons/icon-navigation-road.js'), - }, - { - name: 'icon-navigation-top', - legacy: true, - hidden: true, - path: () => import('./icons/icon-navigation-top.js'), - }, - { - name: 'icon-navigation-up', - legacy: true, - hidden: true, - path: () => import('./icons/icon-navigation-up.js'), - }, - { - name: 'icon-navigation-vertical', - legacy: true, - hidden: true, - path: () => import('./icons/icon-navigation-vertical.js'), - }, - { - name: 'icon-navigation', - legacy: true, - hidden: true, - path: () => import('./icons/icon-navigation.js'), - }, - { - name: 'icon-navigational-arrow', - path: () => import('./icons/icon-navigational-arrow.js'), - }, - { - name: 'icon-network-alt', - path: () => import('./icons/icon-network-alt.js'), - }, - { - name: 'icon-newspaper-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-newspaper-alt.js'), - }, - { - name: 'icon-newspaper', - path: () => import('./icons/icon-newspaper.js'), - }, - { - name: 'icon-next-media', - legacy: true, - hidden: true, - path: () => import('./icons/icon-next-media.js'), - }, - { - name: 'icon-next', - path: () => import('./icons/icon-next.js'), - }, - { - name: 'icon-nodes', - legacy: true, - hidden: true, - path: () => import('./icons/icon-nodes.js'), - }, - { - name: 'icon-notepad-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-notepad-alt.js'), - }, - { - name: 'icon-notepad', - path: () => import('./icons/icon-notepad.js'), - }, - { - name: 'icon-old-key', - path: () => import('./icons/icon-old-key.js'), - }, - { - name: 'icon-old-phone', - legacy: true, - hidden: true, - path: () => import('./icons/icon-old-phone.js'), - }, - { - name: 'icon-omega', - path: () => import('./icons/icon-omega.js'), - }, - { - name: 'icon-operator', - path: () => import('./icons/icon-operator.js'), - }, - { - name: 'icon-ordered-list', - path: () => import('./icons/icon-ordered-list.js'), - }, - { - name: 'icon-origami', - path: () => import('./icons/icon-origami.js'), - }, - { - name: 'icon-out', - path: () => import('./icons/icon-out.js'), - }, - { - name: 'icon-outbox', - legacy: true, - hidden: true, - path: () => import('./icons/icon-outbox.js'), - }, - { - name: 'icon-outdent', - path: () => import('./icons/icon-outdent.js'), - }, - { - name: 'icon-page-add', - path: () => import('./icons/icon-page-add.js'), - }, - { - name: 'icon-page-down', - path: () => import('./icons/icon-page-down.js'), - }, - { - name: 'icon-page-remove', - path: () => import('./icons/icon-page-remove.js'), - }, - { - name: 'icon-page-restricted', - path: () => import('./icons/icon-page-restricted.js'), - }, - { - name: 'icon-page-up', - path: () => import('./icons/icon-page-up.js'), - }, - { - name: 'icon-paint-roller', - legacy: true, - hidden: true, - path: () => import('./icons/icon-paint-roller.js'), - }, - { - name: 'icon-palette', - path: () => import('./icons/icon-palette.js'), - }, - { - name: 'icon-panel-show', - path: () => import('./icons/icon-panel-show.js'), - }, - { - name: 'icon-pannel-close', - path: () => import('./icons/icon-pannel-close.js'), - }, - { - name: 'icon-paper-bag', - legacy: true, - hidden: true, - path: () => import('./icons/icon-paper-bag.js'), - }, - { - name: 'icon-paper-plane-alt', - path: () => import('./icons/icon-paper-plane-alt.js'), - }, - { - name: 'icon-paper-plane', - path: () => import('./icons/icon-paper-plane.js'), - }, - { - name: 'icon-partly-cloudy', - path: () => import('./icons/icon-partly-cloudy.js'), - }, - { - name: 'icon-paragraph', - path: () => import('./icons/icon-paragraph.js'), - }, - { - name: 'icon-paste-in', - legacy: true, - hidden: true, - path: () => import('./icons/icon-paste-in.js'), - }, - { - name: 'icon-pause', - path: () => import('./icons/icon-pause.js'), - }, - { - name: 'icon-pc', - legacy: true, - hidden: true, - path: () => import('./icons/icon-pc.js'), - }, - { - name: 'icon-people-alt-2', - legacy: true, - hidden: true, - path: () => import('./icons/icon-people-alt-2.js'), - }, - { - name: 'icon-people-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-people-alt.js'), - }, - { - name: 'icon-people-female', - legacy: true, - hidden: true, - path: () => import('./icons/icon-people-female.js'), - }, - { - name: 'icon-people', - path: () => import('./icons/icon-people.js'), - }, - { - name: 'icon-phone-ring', - path: () => import('./icons/icon-phone-ring.js'), - }, - { - name: 'icon-phone', - path: () => import('./icons/icon-phone.js'), - }, - { - name: 'icon-photo-album', - path: () => import('./icons/icon-photo-album.js'), - }, - { - name: 'icon-picture', - path: () => import('./icons/icon-picture.js'), - }, - { - name: 'icon-pictures-alt-2', - path: () => import('./icons/icon-pictures-alt-2.js'), - }, - { - name: 'icon-pictures-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-pictures-alt.js'), - }, - { - name: 'icon-pictures', - path: () => import('./icons/icon-pictures.js'), - }, - { - name: 'icon-pie-chart', - path: () => import('./icons/icon-pie-chart.js'), - }, - { - name: 'icon-piggy-bank', - path: () => import('./icons/icon-piggy-bank.js'), - }, - { - name: 'icon-pin-location', - path: () => import('./icons/icon-pin-location.js'), - }, - { - name: 'icon-plane', - path: () => import('./icons/icon-plane.js'), - }, - { - name: 'icon-planet', - legacy: true, - hidden: true, - path: () => import('./icons/icon-planet.js'), - }, - { - name: 'icon-play', - path: () => import('./icons/icon-play.js'), - }, - { - name: 'icon-playing-cards', - legacy: true, - hidden: true, - path: () => import('./icons/icon-playing-cards.js'), - }, - { - name: 'icon-playlist', - path: () => import('./icons/icon-playlist.js'), - }, - { - name: 'icon-plugin', - path: () => import('./icons/icon-plugin.js'), - }, - { - name: 'icon-podcast', - path: () => import('./icons/icon-podcast.js'), - }, - { - name: 'icon-poll', - legacy: true, - hidden: true, - path: () => import('./icons/icon-poll.js'), - }, - { - name: 'icon-post-it', - path: () => import('./icons/icon-post-it.js'), - }, - { - name: 'icon-power-outlet', - legacy: true, - hidden: true, - path: () => import('./icons/icon-power-outlet.js'), - }, - { - name: 'icon-power', - path: () => import('./icons/icon-power.js'), - }, - { - name: 'icon-presentation', - path: () => import('./icons/icon-presentation.js'), - }, - { - name: 'icon-previous-media', - path: () => import('./icons/icon-previous-media.js'), - }, - { - name: 'icon-previous', - path: () => import('./icons/icon-previous.js'), - }, - { - name: 'icon-price-dollar', - legacy: true, - hidden: true, - path: () => import('./icons/icon-price-dollar.js'), - }, - { - name: 'icon-price-euro', - legacy: true, - hidden: true, - path: () => import('./icons/icon-price-euro.js'), - }, - { - name: 'icon-price-pound', - legacy: true, - hidden: true, - path: () => import('./icons/icon-price-pound.js'), - }, - { - name: 'icon-price-yen', - legacy: true, - hidden: true, - path: () => import('./icons/icon-price-yen.js'), - }, - { - name: 'icon-print', - path: () => import('./icons/icon-print.js'), - }, - { - name: 'icon-printer-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-printer-alt.js'), - }, - { - name: 'icon-projector', - path: () => import('./icons/icon-projector.js'), - }, - { - name: 'icon-pulse', - path: () => import('./icons/icon-pulse.js'), - }, - { - name: 'icon-pushpin', - path: () => import('./icons/icon-pushpin.js'), - }, - { - name: 'icon-qr-code', - path: () => import('./icons/icon-qr-code.js'), - }, - { - name: 'icon-quote', - path: () => import('./icons/icon-quote.js'), - }, - { - name: 'icon-radio-alt', - path: () => import('./icons/icon-radio-alt.js'), - }, - { - name: 'icon-radio-receiver', - path: () => import('./icons/icon-radio-receiver.js'), - }, - { - name: 'icon-radio', - path: () => import('./icons/icon-radio.js'), - }, - { - name: 'icon-rain', - path: () => import('./icons/icon-rain.js'), - }, - { - name: 'icon-rate', - legacy: true, - hidden: true, - path: () => import('./icons/icon-rate.js'), - }, - { - name: 'icon-re-post', - path: () => import('./icons/icon-re-post.js'), - }, - { - name: 'icon-readonly', - path: () => import('./icons/icon-readonly.js'), - }, - { - name: 'icon-receipt-alt', - path: () => import('./icons/icon-receipt-alt.js'), - }, - { - name: 'icon-reception', - path: () => import('./icons/icon-reception.js'), - }, - { - name: 'icon-record', - legacy: true, - hidden: true, - path: () => import('./icons/icon-record.js'), - }, - { - name: 'icon-rectangle-ellipsis', - path: () => import('./icons/icon-rectangle-ellipsis.js'), - }, - { - name: 'icon-redo', - path: () => import('./icons/icon-redo.js'), - }, - { - name: 'icon-refresh', - path: () => import('./icons/icon-refresh.js'), - }, - { - name: 'icon-remote', - legacy: true, - hidden: true, - path: () => import('./icons/icon-remote.js'), - }, - { - name: 'icon-remove', - path: () => import('./icons/icon-remove.js'), - }, - { - name: 'icon-repeat-one', - path: () => import('./icons/icon-repeat-one.js'), - }, - { - name: 'icon-repeat', - path: () => import('./icons/icon-repeat.js'), - }, - { - name: 'icon-reply-arrow', - path: () => import('./icons/icon-reply-arrow.js'), - }, - { - name: 'icon-resize', - path: () => import('./icons/icon-resize.js'), - }, - { - name: 'icon-return-to-top', - legacy: true, - hidden: true, - path: () => import('./icons/icon-return-to-top.js'), - }, - { - name: 'icon-right-double-arrow', - legacy: true, - hidden: true, - path: () => import('./icons/icon-right-double-arrow.js'), - }, - { - name: 'icon-roadsign', - legacy: true, - hidden: true, - path: () => import('./icons/icon-roadsign.js'), - }, - { - name: 'icon-rocket', - path: () => import('./icons/icon-rocket.js'), - }, - { - name: 'icon-rss', - path: () => import('./icons/icon-rss.js'), - }, - { - name: 'icon-ruler-alt', - path: () => import('./icons/icon-ruler-alt.js'), - }, - { - name: 'icon-ruler', - path: () => import('./icons/icon-ruler.js'), - }, - { - name: 'icon-satellite-dish', - path: () => import('./icons/icon-satellite-dish.js'), - }, - { - name: 'icon-save', - path: () => import('./icons/icon-save.js'), - }, - { - name: 'icon-scan', - path: () => import('./icons/icon-scan.js'), - }, - { - name: 'icon-school', - path: () => import('./icons/icon-school.js'), - }, - { - name: 'icon-screensharing', - path: () => import('./icons/icon-screensharing.js'), - }, - { - name: 'icon-script-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-script-alt.js'), - }, - { - name: 'icon-script', - path: () => import('./icons/icon-script.js'), - }, - { - name: 'icon-scull', - path: () => import('./icons/icon-scull.js'), - }, - { - name: 'icon-search', - path: () => import('./icons/icon-search.js'), - }, - { - name: 'icon-sensor', - path: () => import('./icons/icon-sensor.js'), - }, - { - name: 'icon-server-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-server-alt.js'), - }, - { - name: 'icon-server', - path: () => import('./icons/icon-server.js'), - }, - { - name: 'icon-settings-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-settings-alt.js'), - }, - { - name: 'icon-settings', - path: () => import('./icons/icon-settings.js'), - }, - { - name: 'icon-share-alt', - path: () => import('./icons/icon-share-alt.js'), - }, - { - name: 'icon-share', - path: () => import('./icons/icon-share.js'), - }, - { - name: 'icon-sharing-iphone', - path: () => import('./icons/icon-sharing-iphone.js'), - }, - { - name: 'icon-shield', - path: () => import('./icons/icon-shield.js'), - }, - { - name: 'icon-shift', - path: () => import('./icons/icon-shift.js'), - }, - { - name: 'icon-shipping-box', - path: () => import('./icons/icon-shipping-box.js'), - }, - { - name: 'icon-shipping', - path: () => import('./icons/icon-shipping.js'), - }, - { - name: 'icon-shoe', - legacy: true, - hidden: true, - path: () => import('./icons/icon-shoe.js'), - }, - { - name: 'icon-shopping-basket-alt-2', - legacy: true, - hidden: true, - path: () => import('./icons/icon-shopping-basket-alt-2.js'), - }, - { - name: 'icon-shopping-basket-alt', - path: () => import('./icons/icon-shopping-basket-alt.js'), - }, - { - name: 'icon-shopping-basket', - path: () => import('./icons/icon-shopping-basket.js'), - }, - { - name: 'icon-shuffle', - path: () => import('./icons/icon-shuffle.js'), - }, - { - name: 'icon-sience', - legacy: true, - hidden: true, - path: () => import('./icons/icon-sience.js'), - }, - { - name: 'icon-science', - path: () => import('./icons/icon-science.js'), - }, - { - name: 'icon-single-note', - path: () => import('./icons/icon-single-note.js'), - }, - { - name: 'icon-sitemap', - legacy: true, - hidden: true, - path: () => import('./icons/icon-sitemap.js'), - }, - { - name: 'icon-sleep', - path: () => import('./icons/icon-sleep.js'), - }, - { - name: 'icon-slideshow', - legacy: true, - hidden: true, - path: () => import('./icons/icon-slideshow.js'), - }, - { - name: 'icon-smiley-inverted', - legacy: true, - hidden: true, - path: () => import('./icons/icon-smiley-inverted.js'), - }, - { - name: 'icon-smiley', - path: () => import('./icons/icon-smiley.js'), - }, - { - name: 'icon-snow', - path: () => import('./icons/icon-snow.js'), - }, - { - name: 'icon-sound-low', - path: () => import('./icons/icon-sound-low.js'), - }, - { - name: 'icon-sound-medium', - legacy: true, - hidden: true, - path: () => import('./icons/icon-sound-medium.js'), - }, - { - name: 'icon-sound-off', - path: () => import('./icons/icon-sound-off.js'), - }, - { - name: 'icon-sound-waves', - path: () => import('./icons/icon-sound-waves.js'), - }, - { - name: 'icon-sound', - path: () => import('./icons/icon-sound.js'), - }, - { - name: 'icon-spades', - path: () => import('./icons/icon-spades.js'), - }, - { - name: 'icon-speaker', - path: () => import('./icons/icon-speaker.js'), - }, - { - name: 'icon-speed-gauge', - path: () => import('./icons/icon-speed-gauge.js'), - }, - { - name: 'icon-split-alt', - path: () => import('./icons/icon-split-alt.js'), - }, - { - name: 'icon-split', - path: () => import('./icons/icon-split.js'), - }, - { - name: 'icon-sprout', - path: () => import('./icons/icon-sprout.js'), - }, - { - name: 'icon-squiggly-line', - legacy: true, - hidden: true, - path: () => import('./icons/icon-squiggly-line.js'), - }, - { - name: 'icon-ssd', - legacy: true, - hidden: true, - path: () => import('./icons/icon-ssd.js'), - }, - { - name: 'icon-stacked-disks', - legacy: true, - hidden: true, - path: () => import('./icons/icon-stacked-disks.js'), - }, - { - name: 'icon-stamp', - legacy: true, - hidden: true, - path: () => import('./icons/icon-stamp.js'), - }, - { - name: 'icon-stop-alt', - path: () => import('./icons/icon-stop-alt.js'), - }, - { - name: 'icon-stop-hand', - legacy: true, - hidden: true, - path: () => import('./icons/icon-stop-hand.js'), - }, - { - name: 'icon-stop', - path: () => import('./icons/icon-stop.js'), - }, - { - name: 'icon-store', - path: () => import('./icons/icon-store.js'), - }, - { - name: 'icon-stream', - legacy: true, - hidden: true, - path: () => import('./icons/icon-stream.js'), - }, - { - name: 'icon-strikethrough', - path: () => import('./icons/icon-strikethrough.js'), - }, - { - name: 'icon-subscript', - path: () => import('./icons/icon-subscript.js'), - }, - { - name: 'icon-superscript', - path: () => import('./icons/icon-superscript.js'), - }, - { - name: 'icon-sunny', - path: () => import('./icons/icon-sunny.js'), - }, - { - name: 'icon-sweatshirt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-sweatshirt.js'), - }, - { - name: 'icon-sync', - path: () => import('./icons/icon-sync.js'), - }, - { - name: 'icon-t-shirt', - path: () => import('./icons/icon-t-shirt.js'), - }, - { - name: 'icon-tab-key', - path: () => import('./icons/icon-tab-key.js'), - }, - { - name: 'icon-table', - path: () => import('./icons/icon-table.js'), - }, - { - name: 'icon-tag', - path: () => import('./icons/icon-tag.js'), - }, - { - name: 'icon-tags', - path: () => import('./icons/icon-tags.js'), - }, - { - name: 'icon-takeaway-cup', - legacy: true, - hidden: true, - path: () => import('./icons/icon-takeaway-cup.js'), - }, - { - name: 'icon-target', - path: () => import('./icons/icon-target.js'), - }, - { - name: 'icon-temperatrure-alt', - path: () => import('./icons/icon-temperatrure-alt.js'), - }, - { - name: 'icon-temperature', - path: () => import('./icons/icon-temperature.js'), - }, - { - name: 'icon-terminal', - path: () => import('./icons/icon-terminal.js'), - }, - { - name: 'icon-text-align-center', - path: () => import('./icons/icon-text-align-center.js'), - }, - { - name: 'icon-text-align-justify', - path: () => import('./icons/icon-text-align-justify.js'), - }, - { - name: 'icon-text-align-left', - path: () => import('./icons/icon-text-align-left.js'), - }, - { - name: 'icon-text-align-right', - path: () => import('./icons/icon-text-align-right.js'), - }, - { - name: 'icon-text-direction-ltr', - path: () => import('./icons/icon-text-direction-ltr.js'), - }, - { - name: 'icon-text-direction-rtl', - path: () => import('./icons/icon-text-direction-rtl.js'), - }, - { - name: 'icon-theater', - path: () => import('./icons/icon-theater.js'), - }, - { - name: 'icon-thumb-down', - path: () => import('./icons/icon-thumb-down.js'), - }, - { - name: 'icon-thumb-up', - path: () => import('./icons/icon-thumb-up.js'), - }, - { - name: 'icon-thumbnail-list', - path: () => import('./icons/icon-thumbnail-list.js'), - }, - { - name: 'icon-thumbnails-small', - path: () => import('./icons/icon-thumbnails-small.js'), - }, - { - name: 'icon-thumbnails', - path: () => import('./icons/icon-thumbnails.js'), - }, - { - name: 'icon-ticket', - path: () => import('./icons/icon-ticket.js'), - }, - { - name: 'icon-time', - path: () => import('./icons/icon-time.js'), - }, - { - name: 'icon-timer', - path: () => import('./icons/icon-timer.js'), - }, - { - name: 'icon-tools', - legacy: true, - hidden: true, - path: () => import('./icons/icon-tools.js'), - }, - { - name: 'icon-top', - legacy: true, - hidden: true, - path: () => import('./icons/icon-top.js'), - }, - { - name: 'icon-traffic-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-traffic-alt.js'), - }, - { - name: 'icon-trafic', - path: () => import('./icons/icon-trafic.js'), - }, - { - name: 'icon-train', - path: () => import('./icons/icon-train.js'), - }, - { - name: 'icon-trash-alt-2', - legacy: true, - hidden: true, - path: () => import('./icons/icon-trash-alt-2.js'), - }, - { - name: 'icon-trash-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-trash-alt.js'), - }, - { - name: 'icon-trash', - path: () => import('./icons/icon-trash.js'), - }, - { - name: 'icon-tree', - path: () => import('./icons/icon-tree.js'), - }, - { - name: 'icon-trophy', - path: () => import('./icons/icon-trophy.js'), - }, - { - name: 'icon-truck', - path: () => import('./icons/icon-truck.js'), - }, - { - name: 'icon-tv-old', - path: () => import('./icons/icon-tv-old.js'), - }, - { - name: 'icon-tv', - path: () => import('./icons/icon-tv.js'), - }, - { - name: 'icon-umb-content', - legacy: true, - hidden: true, - path: () => import('./icons/icon-umb-content.js'), - }, - { - name: 'icon-umb-developer', - legacy: true, - hidden: true, - path: () => import('./icons/icon-umb-developer.js'), - }, - { - name: 'icon-umb-media', - legacy: true, - hidden: true, - path: () => import('./icons/icon-umb-media.js'), - }, - { - name: 'icon-umb-settings', - legacy: true, - hidden: true, - path: () => import('./icons/icon-umb-settings.js'), - }, - { - name: 'icon-umb-users', - legacy: true, - hidden: true, - path: () => import('./icons/icon-umb-users.js'), - }, - { - name: 'icon-umbrella', - path: () => import('./icons/icon-umbrella.js'), - }, - { - name: 'icon-undo', - path: () => import('./icons/icon-undo.js'), - }, - { - name: 'icon-underline', - path: () => import('./icons/icon-underline.js'), - }, - { - name: 'icon-unlink', - path: () => import('./icons/icon-unlink.js'), - }, - { - name: 'icon-unlocked', - path: () => import('./icons/icon-unlocked.js'), - }, - { - name: 'icon-unplug', - path: () => import('./icons/icon-unplug.js'), - }, - { - name: 'icon-untitled', - legacy: true, - hidden: true, - path: () => import('./icons/icon-untitled.js'), - }, - { - name: 'icon-usb-connector', - legacy: true, - hidden: true, - path: () => import('./icons/icon-usb-connector.js'), - }, - { - name: 'icon-usb', - path: () => import('./icons/icon-usb.js'), - }, - { - name: 'icon-user-female', - legacy: true, - hidden: true, - path: () => import('./icons/icon-user-female.js'), - }, - { - name: 'icon-user-females-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-user-females-alt.js'), - }, - { - name: 'icon-user-females', - legacy: true, - hidden: true, - path: () => import('./icons/icon-user-females.js'), - }, - { - name: 'icon-user-glasses', - legacy: true, - hidden: true, - path: () => import('./icons/icon-user-glasses.js'), - }, - { - name: 'icon-user', - path: () => import('./icons/icon-user.js'), - }, - { - name: 'icon-users-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-users-alt.js'), - }, - { - name: 'icon-users', - path: () => import('./icons/icon-users.js'), - }, - { - name: 'icon-utilities', - path: () => import('./icons/icon-utilities.js'), - }, - { - name: 'icon-vcard', - path: () => import('./icons/icon-vcard.js'), - }, - { - name: 'icon-video', - path: () => import('./icons/icon-video.js'), - }, - { - name: 'icon-voice', - path: () => import('./icons/icon-voice.js'), - }, - { - name: 'icon-wall-plug', - path: () => import('./icons/icon-wall-plug.js'), - }, - { - name: 'icon-wallet', - path: () => import('./icons/icon-wallet.js'), - }, - { - name: 'icon-wand', - path: () => import('./icons/icon-wand.js'), - }, - { - name: 'icon-webhook', - path: () => import('./icons/icon-webhook.js'), - }, - { - name: 'icon-weight', - path: () => import('./icons/icon-weight.js'), - }, - { - name: 'icon-width', - path: () => import('./icons/icon-width.js'), - }, - { - name: 'icon-wifi', - path: () => import('./icons/icon-wifi.js'), - }, - { - name: 'icon-window-popin', - path: () => import('./icons/icon-window-popin.js'), - }, - { - name: 'icon-window-popout', - path: () => import('./icons/icon-window-popout.js'), - }, - { - name: 'icon-window-sizes', - path: () => import('./icons/icon-window-sizes.js'), - }, - { - name: 'icon-wine-glass', - path: () => import('./icons/icon-wine-glass.js'), - }, - { - name: 'icon-wrench', - path: () => import('./icons/icon-wrench.js'), - }, - { - name: 'icon-wrong', - path: () => import('./icons/icon-wrong.js'), - }, - { - name: 'icon-zip', - path: () => import('./icons/icon-zip.js'), - }, - { - name: 'icon-zom-out', - legacy: true, - hidden: true, - path: () => import('./icons/icon-zom-out.js'), - }, - { - name: 'icon-zoom-in', - path: () => import('./icons/icon-zoom-in.js'), - }, - { - name: 'icon-zoom-out', - path: () => import('./icons/icon-zoom-out.js'), - }, - { - name: 'icon-star', - path: () => import('./icons/icon-star.js'), - }, - { - name: 'icon-database', - path: () => import('./icons/icon-database.js'), - }, - { - name: 'icon-umb-manifest', - hidden: true, - path: () => import('./icons/icon-umb-manifest.js'), - }, - { - name: 'icon-puzzle-piece', - path: () => import('./icons/icon-puzzle-piece.js'), - }, - { - name: 'icon-document-3d', - path: () => import('./icons/icon-document-3d.js'), - }, - { - name: 'icon-document-medal', - path: () => import('./icons/icon-document-medal.js'), - }, - { - name: 'icon-document-chart-bar', - path: () => import('./icons/icon-document-chart-bar.js'), - }, - { - name: 'icon-document-chart-graph', - path: () => import('./icons/icon-document-chart-graph.js'), - }, - { - name: 'icon-document-html', - path: () => import('./icons/icon-document-html.js'), - }, - { - name: 'icon-document-js', - path: () => import('./icons/icon-document-js.js'), - }, - { - name: 'icon-document-key', - path: () => import('./icons/icon-document-key.js'), - }, - { - name: 'icon-document-search', - path: () => import('./icons/icon-document-search.js'), - }, - { - name: 'icon-document-settings', - path: () => import('./icons/icon-document-settings.js'), - }, - { - name: 'icon-document-spreadsheet', - path: () => import('./icons/icon-document-spreadsheet.js'), - }, - { - name: 'icon-document-command', - path: () => import('./icons/icon-document-command.js'), - }, - { - name: 'icon-document-command', - path: () => import('./icons/icon-document-command.js'), - }, - { - name: 'icon-document-font', - path: () => import('./icons/icon-document-font.js'), - }, - { - name: 'icon-document-user', - path: () => import('./icons/icon-document-user.js'), - }, - { - name: 'icon-document-image', - path: () => import('./icons/icon-document-image.js'), - }, - { - name: 'icon-document-play', - path: () => import('./icons/icon-document-play.js'), - }, - { - name: 'icon-document-play', - path: () => import('./icons/icon-document-play.js'), - }, - { - name: 'icon-shared-value', - path: () => import('./icons/icon-shared-value.js'), - }, - { - name: 'icon-layout-masonry', - path: () => import('./icons/icon-layout-masonry.js'), - }, - { - name: 'icon-layout-grid', - path: () => import('./icons/icon-layout-grid.js'), - }, - { - name: 'icon-layout-list', - path: () => import('./icons/icon-layout-list.js'), - }, - { - name: 'icon-layout-panel-left', - path: () => import('./icons/icon-layout-panel-left.js'), - }, - { - name: 'icon-spray-can', - path: () => import('./icons/icon-spray-can.js'), - }, - { - name: 'icon-swatch-book', - path: () => import('./icons/icon-swatch-book.js'), - }, - { - name: 'icon-shape-cylinder', - path: () => import('./icons/icon-shape-cylinder.js'), - }, - { - name: 'icon-shape-triangle-right', - path: () => import('./icons/icon-shape-triangle-right.js'), - }, - { - name: 'icon-shape-triangle', - path: () => import('./icons/icon-shape-triangle.js'), - }, - { - name: 'icon-shape-circle', - path: () => import('./icons/icon-shape-circle.js'), - }, - { - name: 'icon-shape-square', - path: () => import('./icons/icon-shape-square.js'), - }, - { - name: 'icon-shape-hexagon', - path: () => import('./icons/icon-shape-hexagon.js'), - }, - { - name: 'icon-shape-rectangle-horizontal', - path: () => import('./icons/icon-shape-rectangle-horizontal.js'), - }, - { - name: 'icon-shape-rectangle-vertical', - path: () => import('./icons/icon-shape-rectangle-vertical.js'), - }, - { - name: 'icon-shapes', - path: () => import('./icons/icon-shapes.js'), - }, - { - name: 'icon-layout-dislocated', - path: () => import('./icons/icon-layout-dislocated.js'), - }, - { - name: 'icon-blend', - path: () => import('./icons/icon-blend.js'), - }, - { - name: 'icon-land-plot', - path: () => import('./icons/icon-land-plot.js'), - }, - { - name: 'icon-facebook', - path: () => import('./icons/icon-facebook.js'), - }, - { - name: 'icon-gitbook', - path: () => import('./icons/icon-gitbook.js'), - }, - { - name: 'icon-github', - path: () => import('./icons/icon-github.js'), - }, - { - name: 'icon-gitlab', - path: () => import('./icons/icon-gitlab.js'), - }, - { - name: 'icon-google', - path: () => import('./icons/icon-google.js'), - }, - { - name: 'icon-mastodon', - path: () => import('./icons/icon-mastodon.js'), - }, - { - name: 'icon-twitter-x', - path: () => import('./icons/icon-twitter-x.js'), - }, - { - name: 'icon-art-easel', - legacy: true, - hidden: true, - path: () => import('./icons/icon-art-easel.js'), - }, - { - name: 'icon-article', - legacy: true, - hidden: true, - path: () => import('./icons/icon-article.js'), - }, - { - name: 'icon-auction-hammer', - legacy: true, - hidden: true, - path: () => import('./icons/icon-auction-hammer.js'), - }, - { - name: 'icon-badge-count', - legacy: true, - hidden: true, - path: () => import('./icons/icon-badge-count.js'), - }, - { - name: 'icon-band-aid', - legacy: true, - hidden: true, - path: () => import('./icons/icon-band-aid.js'), - }, - { - name: 'icon-baby-stroller', - legacy: true, - hidden: true, - path: () => import('./icons/icon-baby-stroller.js'), - }, - { - name: 'icon-bill-dollar', - legacy: true, - hidden: true, - path: () => import('./icons/icon-bill-dollar.js'), - }, - { - name: 'icon-bill-euro', - legacy: true, - hidden: true, - path: () => import('./icons/icon-bill-euro.js'), - }, - { - name: 'icon-bill-pound', - legacy: true, - hidden: true, - path: () => import('./icons/icon-bill-pound.js'), - }, - { - name: 'icon-bill-yen', - legacy: true, - hidden: true, - path: () => import('./icons/icon-bill-yen.js'), - }, - { - name: 'icon-bill', - legacy: true, - hidden: true, - path: () => import('./icons/icon-bill.js'), - }, - { - name: 'icon-billboard', - legacy: true, - hidden: true, - path: () => import('./icons/icon-billboard.js'), - }, - { - name: 'icon-bills-dollar', - legacy: true, - hidden: true, - path: () => import('./icons/icon-bills-dollar.js'), - }, - { - name: 'icon-bills-euro', - legacy: true, - hidden: true, - path: () => import('./icons/icon-bills-euro.js'), - }, - { - name: 'icon-bills-pound', - legacy: true, - hidden: true, - path: () => import('./icons/icon-bills-pound.js'), - }, - { - name: 'icon-bills-yen', - legacy: true, - hidden: true, - path: () => import('./icons/icon-bills-yen.js'), - }, - { - name: 'icon-bills', - legacy: true, - hidden: true, - path: () => import('./icons/icon-bills.js'), - }, - { - name: 'icon-blueprint', - legacy: true, - hidden: true, - path: () => import('./icons/icon-blueprint.js'), - }, - { - name: 'icon-bomb', - legacy: true, - hidden: true, - path: () => import('./icons/icon-bomb.js'), - }, - { - name: 'icon-cash-register', - legacy: true, - hidden: true, - path: () => import('./icons/icon-cash-register.js'), - }, - { - name: 'icon-checkbox-dotted-active', - legacy: true, - hidden: true, - path: () => import('./icons/icon-checkbox-dotted-active.js'), - }, - { - name: 'icon-chess', - legacy: true, - hidden: true, - path: () => import('./icons/icon-chess.js'), - }, - { - name: 'icon-circus', - legacy: true, - hidden: true, - path: () => import('./icons/icon-circus.js'), - }, - { - name: 'icon-clothes-hanger', - legacy: true, - hidden: true, - path: () => import('./icons/icon-clothes-hanger.js'), - }, - { - name: 'icon-coin', - legacy: true, - hidden: true, - path: () => import('./icons/icon-coin.js'), - }, - { - name: 'icon-coins-dollar-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-coins-dollar-alt.js'), - }, - { - name: 'icon-coins-dollar', - legacy: true, - hidden: true, - path: () => import('./icons/icon-coins-dollar.js'), - }, - { - name: 'icon-coins-euro-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-coins-euro-alt.js'), - }, - { - name: 'icon-coins-euro', - legacy: true, - hidden: true, - path: () => import('./icons/icon-coins-euro.js'), - }, - { - name: 'icon-coins-pound-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-coins-pound-alt.js'), - }, - { - name: 'icon-coins-pound', - legacy: true, - hidden: true, - path: () => import('./icons/icon-coins-pound.js'), - }, - { - name: 'icon-coins-yen-alt', - legacy: true, - hidden: true, - path: () => import('./icons/icon-coins-yen-alt.js'), - }, - { - name: 'icon-coins-yen', - legacy: true, - hidden: true, - path: () => import('./icons/icon-coins-yen.js'), - }, - { - name: 'icon-comb', - legacy: true, - hidden: true, - path: () => import('./icons/icon-comb.js'), - }, - { - name: 'icon-desk', - legacy: true, - hidden: true, - path: () => import('./icons/icon-desk.js'), - }, - { - name: 'icon-dollar-bag', - legacy: true, - hidden: true, - path: () => import('./icons/icon-dollar-bag.js'), - }, - { - name: 'icon-eject', - legacy: true, - hidden: true, - path: () => import('./icons/icon-eject.js'), - }, - { - name: 'icon-euro-bag', - legacy: true, - hidden: true, - path: () => import('./icons/icon-euro-bag.js'), - }, - { - name: 'icon-female-symbol', - legacy: true, - hidden: true, - path: () => import('./icons/icon-female-symbol.js'), - }, - { - name: 'icon-firewall', - legacy: true, - hidden: true, - path: () => import('./icons/icon-firewall.js'), - }, - { - name: 'icon-folder-open', - legacy: true, - hidden: true, - path: () => import('./icons/icon-folder-open.js'), - }, - { - name: 'icon-folder-outline', - legacy: true, - hidden: true, - path: () => import('./icons/icon-folder-outline.js'), - }, - { - name: 'icon-handprint', - legacy: true, - hidden: true, - path: () => import('./icons/icon-handprint.js'), - }, - { - name: 'icon-hat', - legacy: true, - hidden: true, - path: () => import('./icons/icon-hat.js'), - }, - { - name: 'icon-hd', - legacy: true, - hidden: true, - path: () => import('./icons/icon-hd.js'), - }, - { - name: 'icon-inactive-line', - legacy: true, - hidden: true, - path: () => import('./icons/icon-inactive-line.js'), - }, - { - name: 'icon-keychain', - legacy: true, - hidden: true, - path: () => import('./icons/icon-keychain.js'), - }, - { - name: 'icon-keyhole', - legacy: true, - hidden: true, - path: () => import('./icons/icon-keyhole.js'), - }, - { - name: 'icon-linkedin', - legacy: true, - hidden: true, - path: () => import('./icons/icon-linkedin.js'), - }, - { - name: 'icon-linux-tux', - legacy: true, - hidden: true, - path: () => import('./icons/icon-linux-tux.js'), - }, - { - name: 'icon-male-and-female', - legacy: true, - hidden: true, - path: () => import('./icons/icon-male-and-female.js'), - }, - { - name: 'icon-male-symbol', - legacy: true, - hidden: true, - path: () => import('./icons/icon-male-symbol.js'), - }, - { - name: 'icon-molecular-network', - legacy: true, - hidden: true, - path: () => import('./icons/icon-molecular-network.js'), - }, - { - name: 'icon-molecular', - legacy: true, - hidden: true, - path: () => import('./icons/icon-molecular.js'), - }, - { - name: 'icon-umbraco', - path: () => import('./icons/icon-umbraco.js'), - }, - { - name: 'icon-azure', - legacy: true, - hidden: true, - path: () => import('./icons/icon-azure.js'), - }, - { - name: 'icon-microsoft', - legacy: true, - hidden: true, - path: () => import('./icons/icon-microsoft.js'), - }, - { - name: 'icon-os-x', - legacy: true, - hidden: true, - path: () => import('./icons/icon-os-x.js'), - }, - { - name: 'icon-pants', - legacy: true, - hidden: true, - path: () => import('./icons/icon-pants.js'), - }, - { - name: 'icon-parachute-drop', - legacy: true, - hidden: true, - path: () => import('./icons/icon-parachute-drop.js'), - }, - { - name: 'icon-parental-control', - legacy: true, - hidden: true, - path: () => import('./icons/icon-parental-control.js'), - }, - { - name: 'icon-path', - legacy: true, - hidden: true, - path: () => import('./icons/icon-path.js'), - }, - { - name: 'icon-piracy', - legacy: true, - hidden: true, - path: () => import('./icons/icon-piracy.js'), - }, - { - name: 'icon-poker-chip', - legacy: true, - hidden: true, - path: () => import('./icons/icon-poker-chip.js'), - }, - { - name: 'icon-pound-bag', - legacy: true, - hidden: true, - path: () => import('./icons/icon-pound-bag.js'), - }, - { - name: 'icon-receipt-dollar', - legacy: true, - hidden: true, - path: () => import('./icons/icon-receipt-dollar.js'), - }, - { - name: 'icon-receipt-euro', - legacy: true, - hidden: true, - path: () => import('./icons/icon-receipt-euro.js'), - }, - { - name: 'icon-receipt-pound', - legacy: true, - hidden: true, - path: () => import('./icons/icon-receipt-pound.js'), - }, - { - name: 'icon-receipt-yen', - legacy: true, - hidden: true, - path: () => import('./icons/icon-receipt-yen.js'), - }, - { - name: 'icon-road', - legacy: true, - hidden: true, - path: () => import('./icons/icon-road.js'), - }, - { - name: 'icon-safe', - legacy: true, - hidden: true, - path: () => import('./icons/icon-safe.js'), - }, - { - name: 'icon-safedial', - legacy: true, - hidden: true, - path: () => import('./icons/icon-safedial.js'), - }, - { - name: 'icon-sandbox-toys', - legacy: true, - hidden: true, - path: () => import('./icons/icon-sandbox-toys.js'), - }, - { - name: 'icon-security-camera', - legacy: true, - hidden: true, - path: () => import('./icons/icon-security-camera.js'), - }, - { - name: 'icon-settings-alt-2', - legacy: true, - hidden: true, - path: () => import('./icons/icon-settings-alt-2.js'), - }, - { - name: 'icon-share-alt-2', - legacy: true, - hidden: true, - path: () => import('./icons/icon-share-alt-2.js'), - }, - { - name: 'icon-shorts', - legacy: true, - hidden: true, - path: () => import('./icons/icon-shorts.js'), - }, - { - name: 'icon-simcard', - legacy: true, - hidden: true, - path: () => import('./icons/icon-simcard.js'), - }, - { - name: 'icon-tab', - legacy: true, - hidden: true, - path: () => import('./icons/icon-tab.js'), - }, - { - name: 'icon-tactics', - legacy: true, - hidden: true, - path: () => import('./icons/icon-tactics.js'), - }, - { - name: 'icon-theif', - legacy: true, - hidden: true, - path: () => import('./icons/icon-theif.js'), - }, - { - name: 'icon-thought-bubble', - legacy: true, - hidden: true, - path: () => import('./icons/icon-thought-bubble.js'), - }, - { - name: 'icon-twitter', - legacy: true, - hidden: true, - path: () => import('./icons/icon-twitter.js'), - }, - { - name: 'icon-umb-contour', - legacy: true, - hidden: true, - path: () => import('./icons/icon-umb-contour.js'), - }, - { - name: 'icon-umb-deploy', - legacy: true, - hidden: true, - path: () => import('./icons/icon-umb-deploy.js'), - }, - { - name: 'icon-umb-members', - legacy: true, - hidden: true, - path: () => import('./icons/icon-umb-members.js'), - }, - { - name: 'icon-universal', - legacy: true, - hidden: true, - path: () => import('./icons/icon-universal.js'), - }, - { - name: 'icon-war', - legacy: true, - hidden: true, - path: () => import('./icons/icon-war.js'), - }, - { - name: 'icon-windows', - legacy: true, - hidden: true, - path: () => import('./icons/icon-windows.js'), - }, - { - name: 'icon-yen-bag', - legacy: true, - hidden: true, - path: () => import('./icons/icon-yen-bag.js'), - }, -]; +export default [{ +name: "icon-activity", +path: () => import("./icons/icon-activity.js"), +},{ +name: "icon-add", +path: () => import("./icons/icon-add.js"), +},{ +name: "icon-addressbook", +path: () => import("./icons/icon-addressbook.js"), +},{ +name: "icon-alarm-clock", +path: () => import("./icons/icon-alarm-clock.js"), +},{ +name: "icon-alert-alt", +path: () => import("./icons/icon-alert-alt.js"), +},{ +name: "icon-alert", +path: () => import("./icons/icon-alert.js"), +},{ +name: "icon-alt", +path: () => import("./icons/icon-alt.js"), +},{ +name: "icon-anchor", +path: () => import("./icons/icon-anchor.js"), +},{ +name: "icon-app", +path: () => import("./icons/icon-app.js"), +},{ +name: "icon-application-error", +path: () => import("./icons/icon-application-error.js"), +},{ +name: "icon-application-window-alt", +path: () => import("./icons/icon-application-window-alt.js"), +},{ +name: "icon-application-window", +path: () => import("./icons/icon-application-window.js"), +},{ +name: "icon-arrivals", +path: () => import("./icons/icon-arrivals.js"), +},{ +name: "icon-arrow-down", +path: () => import("./icons/icon-arrow-down.js"), +},{ +name: "icon-arrow-left", +path: () => import("./icons/icon-arrow-left.js"), +},{ +name: "icon-arrow-right", +path: () => import("./icons/icon-arrow-right.js"), +},{ +name: "icon-arrow-up", +path: () => import("./icons/icon-arrow-up.js"), +},{ +name: "icon-attachment", +path: () => import("./icons/icon-attachment.js"), +},{ +name: "icon-audio-lines", +path: () => import("./icons/icon-audio-lines.js"), +},{ +name: "icon-autofill", +path: () => import("./icons/icon-autofill.js"), +},{ +name: "icon-award", +path: () => import("./icons/icon-award.js"), +},{ +name: "icon-axis-rotation-2", +path: () => import("./icons/icon-axis-rotation-2.js"), +},{ +name: "icon-axis-rotation-3", +path: () => import("./icons/icon-axis-rotation-3.js"), +},{ +name: "icon-axis-rotation", +path: () => import("./icons/icon-axis-rotation.js"), +},{ +name: "icon-backspace", +path: () => import("./icons/icon-backspace.js"), +},{ +name: "icon-badge-add", +path: () => import("./icons/icon-badge-add.js"), +},{ +name: "icon-badge-remove", +path: () => import("./icons/icon-badge-remove.js"), +},{ +name: "icon-badge-restricted", +legacy: true, +hidden: true, +path: () => import("./icons/icon-badge-restricted.js"), +},{ +name: "icon-ball", +path: () => import("./icons/icon-ball.js"), +},{ +name: "icon-bar-chart", +path: () => import("./icons/icon-bar-chart.js"), +},{ +name: "icon-barcode", +path: () => import("./icons/icon-barcode.js"), +},{ +name: "icon-bars", +path: () => import("./icons/icon-bars.js"), +},{ +name: "icon-battery-full", +path: () => import("./icons/icon-battery-full.js"), +},{ +name: "icon-battery-low", +path: () => import("./icons/icon-battery-low.js"), +},{ +name: "icon-beer-glass", +path: () => import("./icons/icon-beer-glass.js"), +},{ +name: "icon-bell-off", +path: () => import("./icons/icon-bell-off.js"), +},{ +name: "icon-bell", +path: () => import("./icons/icon-bell.js"), +},{ +name: "icon-binarycode", +path: () => import("./icons/icon-binarycode.js"), +},{ +name: "icon-binoculars", +legacy: true, +hidden: true, +path: () => import("./icons/icon-binoculars.js"), +},{ +name: "icon-bird", +path: () => import("./icons/icon-bird.js"), +},{ +name: "icon-birthday-cake", +path: () => import("./icons/icon-birthday-cake.js"), +},{ +name: "icon-block", +path: () => import("./icons/icon-block.js"), +},{ +name: "icon-blockquote", +path: () => import("./icons/icon-blockquote.js"), +},{ +name: "icon-bluetooth", +path: () => import("./icons/icon-bluetooth.js"), +},{ +name: "icon-boat-shipping", +path: () => import("./icons/icon-boat-shipping.js"), +},{ +name: "icon-bold", +path: () => import("./icons/icon-bold.js"), +},{ +name: "icon-bones", +path: () => import("./icons/icon-bones.js"), +},{ +name: "icon-book-alt-2", +path: () => import("./icons/icon-book-alt-2.js"), +},{ +name: "icon-book-alt", +path: () => import("./icons/icon-book-alt.js"), +},{ +name: "icon-book", +path: () => import("./icons/icon-book.js"), +},{ +name: "icon-bookmark", +path: () => import("./icons/icon-bookmark.js"), +},{ +name: "icon-books", +path: () => import("./icons/icon-books.js"), +},{ +name: "icon-box-alt", +path: () => import("./icons/icon-box-alt.js"), +},{ +name: "icon-box-open", +path: () => import("./icons/icon-box-open.js"), +},{ +name: "icon-box", +path: () => import("./icons/icon-box.js"), +},{ +name: "icon-brackets", +path: () => import("./icons/icon-brackets.js"), +},{ +name: "icon-brick", +path: () => import("./icons/icon-brick.js"), +},{ +name: "icon-briefcase", +path: () => import("./icons/icon-briefcase.js"), +},{ +name: "icon-browser-window", +path: () => import("./icons/icon-browser-window.js"), +},{ +name: "icon-brush-alt-2", +path: () => import("./icons/icon-brush-alt-2.js"), +},{ +name: "icon-brush-alt", +path: () => import("./icons/icon-brush-alt.js"), +},{ +name: "icon-brush", +path: () => import("./icons/icon-brush.js"), +},{ +name: "icon-bug", +path: () => import("./icons/icon-bug.js"), +},{ +name: "icon-bulleted-list", +path: () => import("./icons/icon-bulleted-list.js"), +},{ +name: "icon-burn", +path: () => import("./icons/icon-burn.js"), +},{ +name: "icon-bus", +path: () => import("./icons/icon-bus.js"), +},{ +name: "icon-calculator", +path: () => import("./icons/icon-calculator.js"), +},{ +name: "icon-calendar-alt", +path: () => import("./icons/icon-calendar-alt.js"), +},{ +name: "icon-calendar", +path: () => import("./icons/icon-calendar.js"), +},{ +name: "icon-camcorder", +legacy: true, +hidden: true, +path: () => import("./icons/icon-camcorder.js"), +},{ +name: "icon-camera-roll", +path: () => import("./icons/icon-camera-roll.js"), +},{ +name: "icon-candy", +path: () => import("./icons/icon-candy.js"), +},{ +name: "icon-caps-lock", +path: () => import("./icons/icon-caps-lock.js"), +},{ +name: "icon-car", +path: () => import("./icons/icon-car.js"), +},{ +name: "icon-categories", +path: () => import("./icons/icon-categories.js"), +},{ +name: "icon-certificate", +path: () => import("./icons/icon-certificate.js"), +},{ +name: "icon-chart-curve", +path: () => import("./icons/icon-chart-curve.js"), +},{ +name: "icon-chart", +path: () => import("./icons/icon-chart.js"), +},{ +name: "icon-chat-active", +legacy: true, +hidden: true, +path: () => import("./icons/icon-chat-active.js"), +},{ +name: "icon-chat", +path: () => import("./icons/icon-chat.js"), +},{ +name: "icon-check", +path: () => import("./icons/icon-check.js"), +},{ +name: "icon-checkbox-dotted", +path: () => import("./icons/icon-checkbox-dotted.js"), +},{ +name: "icon-checkbox-empty", +legacy: true, +hidden: true, +path: () => import("./icons/icon-checkbox-empty.js"), +},{ +name: "icon-checkbox", +path: () => import("./icons/icon-checkbox.js"), +},{ +name: "icon-chip-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-chip-alt.js"), +},{ +name: "icon-chip", +path: () => import("./icons/icon-chip.js"), +},{ +name: "icon-cinema", +path: () => import("./icons/icon-cinema.js"), +},{ +name: "icon-circle-dotted-active", +path: () => import("./icons/icon-circle-dotted-active.js"), +},{ +name: "icon-circle-dotted", +path: () => import("./icons/icon-circle-dotted.js"), +},{ +name: "icon-circuits", +path: () => import("./icons/icon-circuits.js"), +},{ +name: "icon-clear-formatting", +path: () => import("./icons/icon-clear-formatting.js"), +},{ +name: "icon-client", +legacy: true, +hidden: true, +path: () => import("./icons/icon-client.js"), +},{ +name: "icon-clipboard", +path: () => import("./icons/icon-clipboard.js"), +},{ +name: "icon-clipboard-copy", +path: () => import("./icons/icon-clipboard-copy.js"), +},{ +name: "icon-clipboard-entry", +path: () => import("./icons/icon-clipboard-entry.js"), +},{ +name: "icon-clipboard-paste", +path: () => import("./icons/icon-clipboard-paste.js"), +},{ +name: "icon-cloud-drive", +path: () => import("./icons/icon-cloud-drive.js"), +},{ +name: "icon-cloud-upload", +path: () => import("./icons/icon-cloud-upload.js"), +},{ +name: "icon-cloud", +path: () => import("./icons/icon-cloud.js"), +},{ +name: "icon-cloudy", +path: () => import("./icons/icon-cloudy.js"), +},{ +name: "icon-clubs", +path: () => import("./icons/icon-clubs.js"), +},{ +name: "icon-cocktail", +path: () => import("./icons/icon-cocktail.js"), +},{ +name: "icon-code", +path: () => import("./icons/icon-code.js"), +},{ +name: "icon-code-xml", +path: () => import("./icons/icon-code-xml.js"), +},{ +name: "icon-coffee", +path: () => import("./icons/icon-coffee.js"), +},{ +name: "icon-coin-dollar", +path: () => import("./icons/icon-coin-dollar.js"), +},{ +name: "icon-coin-euro", +path: () => import("./icons/icon-coin-euro.js"), +},{ +name: "icon-coin-pound", +path: () => import("./icons/icon-coin-pound.js"), +},{ +name: "icon-coin-yen", +path: () => import("./icons/icon-coin-yen.js"), +},{ +name: "icon-coins-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-coins-alt.js"), +},{ +name: "icon-coins", +path: () => import("./icons/icon-coins.js"), +},{ +name: "icon-color-bucket", +path: () => import("./icons/icon-color-bucket.js"), +},{ +name: "icon-colorpicker", +path: () => import("./icons/icon-colorpicker.js"), +},{ +name: "icon-columns", +path: () => import("./icons/icon-columns.js"), +},{ +name: "icon-columns-2", +path: () => import("./icons/icon-columns-2.js"), +},{ +name: "icon-columns-3", +path: () => import("./icons/icon-columns-3.js"), +},{ +name: "icon-columns-4", +path: () => import("./icons/icon-columns-4.js"), +},{ +name: "icon-rows-2", +path: () => import("./icons/icon-rows-2.js"), +},{ +name: "icon-rows-3", +path: () => import("./icons/icon-rows-3.js"), +},{ +name: "icon-rows-4", +path: () => import("./icons/icon-rows-4.js"), +},{ +name: "icon-grid-2", +path: () => import("./icons/icon-grid-2.js"), +},{ +name: "icon-grid-3", +path: () => import("./icons/icon-grid-3.js"), +},{ +name: "icon-combination-lock-open", +path: () => import("./icons/icon-combination-lock-open.js"), +},{ +name: "icon-combination-lock", +path: () => import("./icons/icon-combination-lock.js"), +},{ +name: "icon-command", +path: () => import("./icons/icon-command.js"), +},{ +name: "icon-company", +path: () => import("./icons/icon-company.js"), +},{ +name: "icon-compress", +path: () => import("./icons/icon-compress.js"), +},{ +name: "icon-connection", +path: () => import("./icons/icon-connection.js"), +},{ +name: "icon-console", +path: () => import("./icons/icon-console.js"), +},{ +name: "icon-contrast", +path: () => import("./icons/icon-contrast.js"), +},{ +name: "icon-conversation-alt", +path: () => import("./icons/icon-conversation-alt.js"), +},{ +name: "icon-conversation", +legacy: true, +hidden: true, +path: () => import("./icons/icon-conversation.js"), +},{ +name: "icon-coverflow", +path: () => import("./icons/icon-coverflow.js"), +},{ +name: "icon-credit-card-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-credit-card-alt.js"), +},{ +name: "icon-credit-card", +path: () => import("./icons/icon-credit-card.js"), +},{ +name: "icon-crop", +path: () => import("./icons/icon-crop.js"), +},{ +name: "icon-crosshair", +path: () => import("./icons/icon-crosshair.js"), +},{ +name: "icon-crown-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-crown-alt.js"), +},{ +name: "icon-crown", +path: () => import("./icons/icon-crown.js"), +},{ +name: "icon-cupcake", +legacy: true, +hidden: true, +path: () => import("./icons/icon-cupcake.js"), +},{ +name: "icon-curve", +path: () => import("./icons/icon-curve.js"), +},{ +name: "icon-cut", +path: () => import("./icons/icon-cut.js"), +},{ +name: "icon-dashboard", +path: () => import("./icons/icon-dashboard.js"), +},{ +name: "icon-defrag", +path: () => import("./icons/icon-defrag.js"), +},{ +name: "icon-delete-key", +path: () => import("./icons/icon-delete-key.js"), +},{ +name: "icon-delete", +path: () => import("./icons/icon-delete.js"), +},{ +name: "icon-departure", +path: () => import("./icons/icon-departure.js"), +},{ +name: "icon-desktop", +legacy: true, +hidden: true, +path: () => import("./icons/icon-desktop.js"), +},{ +name: "icon-diagnostics", +path: () => import("./icons/icon-diagnostics.js"), +},{ +name: "icon-diagonal-arrow-alt", +path: () => import("./icons/icon-diagonal-arrow-alt.js"), +},{ +name: "icon-diagonal-arrow", +path: () => import("./icons/icon-diagonal-arrow.js"), +},{ +name: "icon-diamond", +path: () => import("./icons/icon-diamond.js"), +},{ +name: "icon-diamonds", +path: () => import("./icons/icon-diamonds.js"), +},{ +name: "icon-dice", +path: () => import("./icons/icon-dice.js"), +},{ +name: "icon-diploma-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-diploma-alt.js"), +},{ +name: "icon-diploma", +path: () => import("./icons/icon-diploma.js"), +},{ +name: "icon-directions-alt", +path: () => import("./icons/icon-directions-alt.js"), +},{ +name: "icon-directions", +path: () => import("./icons/icon-directions.js"), +},{ +name: "icon-disc", +path: () => import("./icons/icon-disc.js"), +},{ +name: "icon-disk-image", +path: () => import("./icons/icon-disk-image.js"), +},{ +name: "icon-display", +path: () => import("./icons/icon-display.js"), +},{ +name: "icon-dna", +path: () => import("./icons/icon-dna.js"), +},{ +name: "icon-dock-connector", +path: () => import("./icons/icon-dock-connector.js"), +},{ +name: "icon-document-dashed-line", +legacy: true, +hidden: true, +path: () => import("./icons/icon-document-dashed-line.js"), +},{ +name: "icon-document", +path: () => import("./icons/icon-document.js"), +},{ +name: "icon-documents", +path: () => import("./icons/icon-documents.js"), +},{ +name: "icon-donate", +legacy: true, +hidden: true, +path: () => import("./icons/icon-donate.js"), +},{ +name: "icon-door-open-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-door-open-alt.js"), +},{ +name: "icon-door-open", +path: () => import("./icons/icon-door-open.js"), +},{ +name: "icon-download-alt", +path: () => import("./icons/icon-download-alt.js"), +},{ +name: "icon-download", +path: () => import("./icons/icon-download.js"), +},{ +name: "icon-drop", +path: () => import("./icons/icon-drop.js"), +},{ +name: "icon-eco", +path: () => import("./icons/icon-eco.js"), +},{ +name: "icon-economy", +legacy: true, +hidden: true, +path: () => import("./icons/icon-economy.js"), +},{ +name: "icon-edit", +path: () => import("./icons/icon-edit.js"), +},{ +name: "icon-embed", +path: () => import("./icons/icon-embed.js"), +},{ +name: "icon-employee", +legacy: true, +hidden: true, +path: () => import("./icons/icon-employee.js"), +},{ +name: "icon-energy-saving-bulb", +path: () => import("./icons/icon-energy-saving-bulb.js"), +},{ +name: "icon-enter", +path: () => import("./icons/icon-enter.js"), +},{ +name: "icon-equalizer", +path: () => import("./icons/icon-equalizer.js"), +},{ +name: "icon-escape", +path: () => import("./icons/icon-escape.js"), +},{ +name: "icon-ethernet", +path: () => import("./icons/icon-ethernet.js"), +},{ +name: "icon-eye", +path: () => import("./icons/icon-eye.js"), +},{ +name: "icon-exit-fullscreen", +path: () => import("./icons/icon-exit-fullscreen.js"), +},{ +name: "icon-facebook-like", +path: () => import("./icons/icon-facebook-like.js"), +},{ +name: "icon-factory", +path: () => import("./icons/icon-factory.js"), +},{ +name: "icon-favorite", +path: () => import("./icons/icon-favorite.js"), +},{ +name: "icon-file-cabinet", +path: () => import("./icons/icon-file-cabinet.js"), +},{ +name: "icon-files", +path: () => import("./icons/icon-files.js"), +},{ +name: "icon-filter-arrows", +path: () => import("./icons/icon-filter-arrows.js"), +},{ +name: "icon-filter", +path: () => import("./icons/icon-filter.js"), +},{ +name: "icon-fingerprint", +path: () => import("./icons/icon-fingerprint.js"), +},{ +name: "icon-fire", +path: () => import("./icons/icon-fire.js"), +},{ +name: "icon-firewire", +legacy: true, +hidden: true, +path: () => import("./icons/icon-firewire.js"), +},{ +name: "icon-flag-alt", +path: () => import("./icons/icon-flag-alt.js"), +},{ +name: "icon-flag", +path: () => import("./icons/icon-flag.js"), +},{ +name: "icon-flash", +path: () => import("./icons/icon-flash.js"), +},{ +name: "icon-flashlight", +path: () => import("./icons/icon-flashlight.js"), +},{ +name: "icon-flowerpot", +path: () => import("./icons/icon-flowerpot.js"), +},{ +name: "icon-folder", +path: () => import("./icons/icon-folder.js"), +},{ +name: "icon-folders", +path: () => import("./icons/icon-folders.js"), +},{ +name: "icon-font", +path: () => import("./icons/icon-font.js"), +},{ +name: "icon-food", +path: () => import("./icons/icon-food.js"), +},{ +name: "icon-footprints", +path: () => import("./icons/icon-footprints.js"), +},{ +name: "icon-forking", +path: () => import("./icons/icon-forking.js"), +},{ +name: "icon-frame-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-frame-alt.js"), +},{ +name: "icon-frame", +path: () => import("./icons/icon-frame.js"), +},{ +name: "icon-fullscreen-alt", +path: () => import("./icons/icon-fullscreen-alt.js"), +},{ +name: "icon-fullscreen", +path: () => import("./icons/icon-fullscreen.js"), +},{ +name: "icon-game", +path: () => import("./icons/icon-game.js"), +},{ +name: "icon-geometry", +legacy: true, +hidden: true, +path: () => import("./icons/icon-geometry.js"), +},{ +name: "icon-gift", +path: () => import("./icons/icon-gift.js"), +},{ +name: "icon-glasses", +path: () => import("./icons/icon-glasses.js"), +},{ +name: "icon-globe-alt", +path: () => import("./icons/icon-globe-alt.js"), +},{ +name: "icon-globe-asia", +legacy: true, +hidden: true, +path: () => import("./icons/icon-globe-asia.js"), +},{ +name: "icon-globe-europe-africa", +legacy: true, +hidden: true, +path: () => import("./icons/icon-globe-europe-africa.js"), +},{ +name: "icon-globe-inverted-america", +legacy: true, +hidden: true, +path: () => import("./icons/icon-globe-inverted-america.js"), +},{ +name: "icon-globe-inverted-asia", +legacy: true, +hidden: true, +path: () => import("./icons/icon-globe-inverted-asia.js"), +},{ +name: "icon-globe-inverted-europe-africa", +legacy: true, +hidden: true, +path: () => import("./icons/icon-globe-inverted-europe-africa.js"), +},{ +name: "icon-globe", +path: () => import("./icons/icon-globe.js"), +},{ +name: "icon-gps", +path: () => import("./icons/icon-gps.js"), +},{ +name: "icon-graduate", +path: () => import("./icons/icon-graduate.js"), +},{ +name: "icon-grid", +path: () => import("./icons/icon-grid.js"), +},{ +name: "icon-grip", +hidden: true, +path: () => import("./icons/icon-grip.js"), +},{ +name: "icon-hammer", +path: () => import("./icons/icon-hammer.js"), +},{ +name: "icon-hand-active-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-hand-active-alt.js"), +},{ +name: "icon-hand-active", +path: () => import("./icons/icon-hand-active.js"), +},{ +name: "icon-hand-pointer-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-hand-pointer-alt.js"), +},{ +name: "icon-hand-pointer", +path: () => import("./icons/icon-hand-pointer.js"), +},{ +name: "icon-handshake", +path: () => import("./icons/icon-handshake.js"), +},{ +name: "icon-handtool-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-handtool-alt.js"), +},{ +name: "icon-handtool", +path: () => import("./icons/icon-handtool.js"), +},{ +name: "icon-hard-drive-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-hard-drive-alt.js"), +},{ +name: "icon-hard-drive", +legacy: true, +hidden: true, +path: () => import("./icons/icon-hard-drive.js"), +},{ +name: "icon-heading-1", +path: () => import("./icons/icon-heading-1.js"), +},{ +name: "icon-heading-2", +path: () => import("./icons/icon-heading-2.js"), +},{ +name: "icon-heading-3", +path: () => import("./icons/icon-heading-3.js"), +},{ +name: "icon-heading-4", +path: () => import("./icons/icon-heading-4.js"), +},{ +name: "icon-headphones", +path: () => import("./icons/icon-headphones.js"), +},{ +name: "icon-headset", +legacy: true, +hidden: true, +path: () => import("./icons/icon-headset.js"), +},{ +name: "icon-hearts", +path: () => import("./icons/icon-hearts.js"), +},{ +name: "icon-height", +path: () => import("./icons/icon-height.js"), +},{ +name: "icon-help-alt", +path: () => import("./icons/icon-help-alt.js"), +},{ +name: "icon-help", +path: () => import("./icons/icon-help.js"), +},{ +name: "icon-history", +path: () => import("./icons/icon-history.js"), +},{ +name: "icon-home", +path: () => import("./icons/icon-home.js"), +},{ +name: "icon-horizontal-rule", +path: () => import("./icons/icon-horizontal-rule.js"), +},{ +name: "icon-hourglass", +path: () => import("./icons/icon-hourglass.js"), +},{ +name: "icon-imac", +legacy: true, +hidden: true, +path: () => import("./icons/icon-imac.js"), +},{ +name: "icon-image-up", +path: () => import("./icons/icon-image-up.js"), +},{ +name: "icon-inbox-full", +legacy: true, +hidden: true, +path: () => import("./icons/icon-inbox-full.js"), +},{ +name: "icon-inbox", +path: () => import("./icons/icon-inbox.js"), +},{ +name: "icon-indent", +path: () => import("./icons/icon-indent.js"), +},{ +name: "icon-infinity", +path: () => import("./icons/icon-infinity.js"), +},{ +name: "icon-info", +path: () => import("./icons/icon-info.js"), +},{ +name: "icon-invoice", +legacy: true, +hidden: true, +path: () => import("./icons/icon-invoice.js"), +},{ +name: "icon-ipad", +legacy: true, +hidden: true, +path: () => import("./icons/icon-ipad.js"), +},{ +name: "icon-iphone", +legacy: true, +hidden: true, +path: () => import("./icons/icon-iphone.js"), +},{ +name: "icon-italic", +path: () => import("./icons/icon-italic.js"), +},{ +name: "icon-item-arrangement", +legacy: true, +hidden: true, +path: () => import("./icons/icon-item-arrangement.js"), +},{ +name: "icon-junk", +path: () => import("./icons/icon-junk.js"), +},{ +name: "icon-key", +path: () => import("./icons/icon-key.js"), +},{ +name: "icon-keyboard", +path: () => import("./icons/icon-keyboard.js"), +},{ +name: "icon-lab", +path: () => import("./icons/icon-lab.js"), +},{ +name: "icon-laptop", +path: () => import("./icons/icon-laptop.js"), +},{ +name: "icon-layers-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-layers-alt.js"), +},{ +name: "icon-layers", +path: () => import("./icons/icon-layers.js"), +},{ +name: "icon-layout", +path: () => import("./icons/icon-layout.js"), +},{ +name: "icon-left-double-arrow", +path: () => import("./icons/icon-left-double-arrow.js"), +},{ +name: "icon-legal", +path: () => import("./icons/icon-legal.js"), +},{ +name: "icon-lense", +legacy: true, +hidden: true, +path: () => import("./icons/icon-lense.js"), +},{ +name: "icon-library", +path: () => import("./icons/icon-library.js"), +},{ +name: "icon-light-down", +path: () => import("./icons/icon-light-down.js"), +},{ +name: "icon-light-up", +path: () => import("./icons/icon-light-up.js"), +},{ +name: "icon-lightbulb-active", +path: () => import("./icons/icon-lightbulb-active.js"), +},{ +name: "icon-lightbulb", +legacy: true, +hidden: true, +path: () => import("./icons/icon-lightbulb.js"), +},{ +name: "icon-lightning", +path: () => import("./icons/icon-lightning.js"), +},{ +name: "icon-link", +path: () => import("./icons/icon-link.js"), +},{ +name: "icon-list", +path: () => import("./icons/icon-list.js"), +},{ +name: "icon-load", +legacy: true, +hidden: true, +path: () => import("./icons/icon-load.js"), +},{ +name: "icon-loading", +legacy: true, +hidden: true, +path: () => import("./icons/icon-loading.js"), +},{ +name: "icon-locate", +path: () => import("./icons/icon-locate.js"), +},{ +name: "icon-location-near-me", +legacy: true, +hidden: true, +path: () => import("./icons/icon-location-near-me.js"), +},{ +name: "icon-location-nearby", +path: () => import("./icons/icon-location-nearby.js"), +},{ +name: "icon-lock", +path: () => import("./icons/icon-lock.js"), +},{ +name: "icon-log-out", +path: () => import("./icons/icon-log-out.js"), +},{ +name: "icon-logout", +legacy: true, +hidden: true, +path: () => import("./icons/icon-logout.js"), +},{ +name: "icon-loupe", +legacy: true, +hidden: true, +path: () => import("./icons/icon-loupe.js"), +},{ +name: "icon-magnet", +path: () => import("./icons/icon-magnet.js"), +},{ +name: "icon-mailbox", +path: () => import("./icons/icon-mailbox.js"), +},{ +name: "icon-map-alt", +path: () => import("./icons/icon-map-alt.js"), +},{ +name: "icon-map-location", +legacy: true, +hidden: true, +path: () => import("./icons/icon-map-location.js"), +},{ +name: "icon-map-marker", +path: () => import("./icons/icon-map-marker.js"), +},{ +name: "icon-map", +path: () => import("./icons/icon-map.js"), +},{ +name: "icon-medal", +path: () => import("./icons/icon-medal.js"), +},{ +name: "icon-medical-emergency", +path: () => import("./icons/icon-medical-emergency.js"), +},{ +name: "icon-medicine", +path: () => import("./icons/icon-medicine.js"), +},{ +name: "icon-meeting", +legacy: true, +hidden: true, +path: () => import("./icons/icon-meeting.js"), +},{ +name: "icon-megaphone", +path: () => import("./icons/icon-megaphone.js"), +},{ +name: "icon-merge", +path: () => import("./icons/icon-merge.js"), +},{ +name: "icon-message-open", +path: () => import("./icons/icon-message-open.js"), +},{ +name: "icon-message-unopened", +legacy: true, +hidden: true, +path: () => import("./icons/icon-message-unopened.js"), +},{ +name: "icon-message", +path: () => import("./icons/icon-message.js"), +},{ +name: "icon-microscope", +path: () => import("./icons/icon-microscope.js"), +},{ +name: "icon-mindmap", +legacy: true, +hidden: true, +path: () => import("./icons/icon-mindmap.js"), +},{ +name: "icon-mobile", +path: () => import("./icons/icon-mobile.js"), +},{ +name: "icon-mountain", +path: () => import("./icons/icon-mountain.js"), +},{ +name: "icon-mouse-cursor", +path: () => import("./icons/icon-mouse-cursor.js"), +},{ +name: "icon-mouse", +path: () => import("./icons/icon-mouse.js"), +},{ +name: "icon-movie-alt", +path: () => import("./icons/icon-movie-alt.js"), +},{ +name: "icon-movie", +path: () => import("./icons/icon-movie.js"), +},{ +name: "icon-multiple-credit-cards", +path: () => import("./icons/icon-multiple-credit-cards.js"), +},{ +name: "icon-multiple-windows", +path: () => import("./icons/icon-multiple-windows.js"), +},{ +name: "icon-music", +path: () => import("./icons/icon-music.js"), +},{ +name: "icon-name-badge", +legacy: true, +hidden: true, +path: () => import("./icons/icon-name-badge.js"), +},{ +name: "icon-navigation-bottom", +legacy: true, +hidden: true, +path: () => import("./icons/icon-navigation-bottom.js"), +},{ +name: "icon-navigation-down", +legacy: true, +hidden: true, +path: () => import("./icons/icon-navigation-down.js"), +},{ +name: "icon-navigation-first", +legacy: true, +hidden: true, +path: () => import("./icons/icon-navigation-first.js"), +},{ +name: "icon-navigation-horizontal", +legacy: true, +hidden: true, +path: () => import("./icons/icon-navigation-horizontal.js"), +},{ +name: "icon-navigation-last", +legacy: true, +hidden: true, +path: () => import("./icons/icon-navigation-last.js"), +},{ +name: "icon-navigation-left", +legacy: true, +hidden: true, +path: () => import("./icons/icon-navigation-left.js"), +},{ +name: "icon-navigation-right", +legacy: true, +hidden: true, +path: () => import("./icons/icon-navigation-right.js"), +},{ +name: "icon-navigation-road", +legacy: true, +hidden: true, +path: () => import("./icons/icon-navigation-road.js"), +},{ +name: "icon-navigation-top", +legacy: true, +hidden: true, +path: () => import("./icons/icon-navigation-top.js"), +},{ +name: "icon-navigation-up", +legacy: true, +hidden: true, +path: () => import("./icons/icon-navigation-up.js"), +},{ +name: "icon-navigation-vertical", +legacy: true, +hidden: true, +path: () => import("./icons/icon-navigation-vertical.js"), +},{ +name: "icon-navigation", +legacy: true, +hidden: true, +path: () => import("./icons/icon-navigation.js"), +},{ +name: "icon-navigational-arrow", +path: () => import("./icons/icon-navigational-arrow.js"), +},{ +name: "icon-network-alt", +path: () => import("./icons/icon-network-alt.js"), +},{ +name: "icon-newspaper-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-newspaper-alt.js"), +},{ +name: "icon-newspaper", +path: () => import("./icons/icon-newspaper.js"), +},{ +name: "icon-next-media", +legacy: true, +hidden: true, +path: () => import("./icons/icon-next-media.js"), +},{ +name: "icon-next", +path: () => import("./icons/icon-next.js"), +},{ +name: "icon-nodes", +legacy: true, +hidden: true, +path: () => import("./icons/icon-nodes.js"), +},{ +name: "icon-notepad-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-notepad-alt.js"), +},{ +name: "icon-notepad", +path: () => import("./icons/icon-notepad.js"), +},{ +name: "icon-old-key", +path: () => import("./icons/icon-old-key.js"), +},{ +name: "icon-old-phone", +legacy: true, +hidden: true, +path: () => import("./icons/icon-old-phone.js"), +},{ +name: "icon-omega", +path: () => import("./icons/icon-omega.js"), +},{ +name: "icon-operator", +path: () => import("./icons/icon-operator.js"), +},{ +name: "icon-ordered-list", +path: () => import("./icons/icon-ordered-list.js"), +},{ +name: "icon-origami", +path: () => import("./icons/icon-origami.js"), +},{ +name: "icon-out", +path: () => import("./icons/icon-out.js"), +},{ +name: "icon-outbox", +legacy: true, +hidden: true, +path: () => import("./icons/icon-outbox.js"), +},{ +name: "icon-outdent", +path: () => import("./icons/icon-outdent.js"), +},{ +name: "icon-page-add", +path: () => import("./icons/icon-page-add.js"), +},{ +name: "icon-page-down", +path: () => import("./icons/icon-page-down.js"), +},{ +name: "icon-page-remove", +path: () => import("./icons/icon-page-remove.js"), +},{ +name: "icon-page-restricted", +path: () => import("./icons/icon-page-restricted.js"), +},{ +name: "icon-page-up", +path: () => import("./icons/icon-page-up.js"), +},{ +name: "icon-paint-roller", +legacy: true, +hidden: true, +path: () => import("./icons/icon-paint-roller.js"), +},{ +name: "icon-palette", +path: () => import("./icons/icon-palette.js"), +},{ +name: "icon-panel-show", +path: () => import("./icons/icon-panel-show.js"), +},{ +name: "icon-pannel-close", +path: () => import("./icons/icon-pannel-close.js"), +},{ +name: "icon-paper-bag", +legacy: true, +hidden: true, +path: () => import("./icons/icon-paper-bag.js"), +},{ +name: "icon-paper-plane-alt", +path: () => import("./icons/icon-paper-plane-alt.js"), +},{ +name: "icon-paper-plane", +path: () => import("./icons/icon-paper-plane.js"), +},{ +name: "icon-partly-cloudy", +path: () => import("./icons/icon-partly-cloudy.js"), +},{ +name: "icon-paragraph", +path: () => import("./icons/icon-paragraph.js"), +},{ +name: "icon-paste-in", +legacy: true, +hidden: true, +path: () => import("./icons/icon-paste-in.js"), +},{ +name: "icon-pause", +path: () => import("./icons/icon-pause.js"), +},{ +name: "icon-pc", +legacy: true, +hidden: true, +path: () => import("./icons/icon-pc.js"), +},{ +name: "icon-people-alt-2", +legacy: true, +hidden: true, +path: () => import("./icons/icon-people-alt-2.js"), +},{ +name: "icon-people-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-people-alt.js"), +},{ +name: "icon-people-female", +legacy: true, +hidden: true, +path: () => import("./icons/icon-people-female.js"), +},{ +name: "icon-people", +path: () => import("./icons/icon-people.js"), +},{ +name: "icon-phone-ring", +path: () => import("./icons/icon-phone-ring.js"), +},{ +name: "icon-phone", +path: () => import("./icons/icon-phone.js"), +},{ +name: "icon-photo-album", +path: () => import("./icons/icon-photo-album.js"), +},{ +name: "icon-picture", +path: () => import("./icons/icon-picture.js"), +},{ +name: "icon-pictures-alt-2", +path: () => import("./icons/icon-pictures-alt-2.js"), +},{ +name: "icon-pictures-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-pictures-alt.js"), +},{ +name: "icon-pictures", +path: () => import("./icons/icon-pictures.js"), +},{ +name: "icon-pie-chart", +path: () => import("./icons/icon-pie-chart.js"), +},{ +name: "icon-piggy-bank", +path: () => import("./icons/icon-piggy-bank.js"), +},{ +name: "icon-pin-location", +path: () => import("./icons/icon-pin-location.js"), +},{ +name: "icon-plane", +path: () => import("./icons/icon-plane.js"), +},{ +name: "icon-planet", +legacy: true, +hidden: true, +path: () => import("./icons/icon-planet.js"), +},{ +name: "icon-play", +path: () => import("./icons/icon-play.js"), +},{ +name: "icon-playing-cards", +legacy: true, +hidden: true, +path: () => import("./icons/icon-playing-cards.js"), +},{ +name: "icon-playlist", +path: () => import("./icons/icon-playlist.js"), +},{ +name: "icon-plugin", +path: () => import("./icons/icon-plugin.js"), +},{ +name: "icon-podcast", +path: () => import("./icons/icon-podcast.js"), +},{ +name: "icon-poll", +legacy: true, +hidden: true, +path: () => import("./icons/icon-poll.js"), +},{ +name: "icon-post-it", +path: () => import("./icons/icon-post-it.js"), +},{ +name: "icon-power-outlet", +legacy: true, +hidden: true, +path: () => import("./icons/icon-power-outlet.js"), +},{ +name: "icon-power", +path: () => import("./icons/icon-power.js"), +},{ +name: "icon-presentation", +path: () => import("./icons/icon-presentation.js"), +},{ +name: "icon-previous-media", +path: () => import("./icons/icon-previous-media.js"), +},{ +name: "icon-previous", +path: () => import("./icons/icon-previous.js"), +},{ +name: "icon-price-dollar", +legacy: true, +hidden: true, +path: () => import("./icons/icon-price-dollar.js"), +},{ +name: "icon-price-euro", +legacy: true, +hidden: true, +path: () => import("./icons/icon-price-euro.js"), +},{ +name: "icon-price-pound", +legacy: true, +hidden: true, +path: () => import("./icons/icon-price-pound.js"), +},{ +name: "icon-price-yen", +legacy: true, +hidden: true, +path: () => import("./icons/icon-price-yen.js"), +},{ +name: "icon-print", +path: () => import("./icons/icon-print.js"), +},{ +name: "icon-printer-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-printer-alt.js"), +},{ +name: "icon-projector", +path: () => import("./icons/icon-projector.js"), +},{ +name: "icon-pulse", +path: () => import("./icons/icon-pulse.js"), +},{ +name: "icon-pushpin", +path: () => import("./icons/icon-pushpin.js"), +},{ +name: "icon-qr-code", +path: () => import("./icons/icon-qr-code.js"), +},{ +name: "icon-quote", +path: () => import("./icons/icon-quote.js"), +},{ +name: "icon-radio-alt", +path: () => import("./icons/icon-radio-alt.js"), +},{ +name: "icon-radio-receiver", +path: () => import("./icons/icon-radio-receiver.js"), +},{ +name: "icon-radio", +path: () => import("./icons/icon-radio.js"), +},{ +name: "icon-rain", +path: () => import("./icons/icon-rain.js"), +},{ +name: "icon-rate", +legacy: true, +hidden: true, +path: () => import("./icons/icon-rate.js"), +},{ +name: "icon-re-post", +path: () => import("./icons/icon-re-post.js"), +},{ +name: "icon-readonly", +path: () => import("./icons/icon-readonly.js"), +},{ +name: "icon-receipt-alt", +path: () => import("./icons/icon-receipt-alt.js"), +},{ +name: "icon-reception", +path: () => import("./icons/icon-reception.js"), +},{ +name: "icon-record", +legacy: true, +hidden: true, +path: () => import("./icons/icon-record.js"), +},{ +name: "icon-rectangle-ellipsis", +path: () => import("./icons/icon-rectangle-ellipsis.js"), +},{ +name: "icon-redo", +path: () => import("./icons/icon-redo.js"), +},{ +name: "icon-refresh", +path: () => import("./icons/icon-refresh.js"), +},{ +name: "icon-remote", +legacy: true, +hidden: true, +path: () => import("./icons/icon-remote.js"), +},{ +name: "icon-remove", +path: () => import("./icons/icon-remove.js"), +},{ +name: "icon-repeat-one", +path: () => import("./icons/icon-repeat-one.js"), +},{ +name: "icon-repeat", +path: () => import("./icons/icon-repeat.js"), +},{ +name: "icon-reply-arrow", +path: () => import("./icons/icon-reply-arrow.js"), +},{ +name: "icon-resize", +path: () => import("./icons/icon-resize.js"), +},{ +name: "icon-return-to-top", +legacy: true, +hidden: true, +path: () => import("./icons/icon-return-to-top.js"), +},{ +name: "icon-right-double-arrow", +legacy: true, +hidden: true, +path: () => import("./icons/icon-right-double-arrow.js"), +},{ +name: "icon-roadsign", +legacy: true, +hidden: true, +path: () => import("./icons/icon-roadsign.js"), +},{ +name: "icon-rocket", +path: () => import("./icons/icon-rocket.js"), +},{ +name: "icon-rss", +path: () => import("./icons/icon-rss.js"), +},{ +name: "icon-ruler-alt", +path: () => import("./icons/icon-ruler-alt.js"), +},{ +name: "icon-ruler", +path: () => import("./icons/icon-ruler.js"), +},{ +name: "icon-satellite-dish", +path: () => import("./icons/icon-satellite-dish.js"), +},{ +name: "icon-save", +path: () => import("./icons/icon-save.js"), +},{ +name: "icon-scan", +path: () => import("./icons/icon-scan.js"), +},{ +name: "icon-school", +path: () => import("./icons/icon-school.js"), +},{ +name: "icon-screensharing", +path: () => import("./icons/icon-screensharing.js"), +},{ +name: "icon-script-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-script-alt.js"), +},{ +name: "icon-script", +path: () => import("./icons/icon-script.js"), +},{ +name: "icon-scull", +path: () => import("./icons/icon-scull.js"), +},{ +name: "icon-search", +path: () => import("./icons/icon-search.js"), +},{ +name: "icon-sensor", +path: () => import("./icons/icon-sensor.js"), +},{ +name: "icon-server-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-server-alt.js"), +},{ +name: "icon-server", +path: () => import("./icons/icon-server.js"), +},{ +name: "icon-settings-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-settings-alt.js"), +},{ +name: "icon-settings", +path: () => import("./icons/icon-settings.js"), +},{ +name: "icon-share-alt", +path: () => import("./icons/icon-share-alt.js"), +},{ +name: "icon-share", +path: () => import("./icons/icon-share.js"), +},{ +name: "icon-sharing-iphone", +path: () => import("./icons/icon-sharing-iphone.js"), +},{ +name: "icon-shield", +path: () => import("./icons/icon-shield.js"), +},{ +name: "icon-shift", +path: () => import("./icons/icon-shift.js"), +},{ +name: "icon-shipping-box", +path: () => import("./icons/icon-shipping-box.js"), +},{ +name: "icon-shipping", +path: () => import("./icons/icon-shipping.js"), +},{ +name: "icon-shoe", +legacy: true, +hidden: true, +path: () => import("./icons/icon-shoe.js"), +},{ +name: "icon-shopping-basket-alt-2", +legacy: true, +hidden: true, +path: () => import("./icons/icon-shopping-basket-alt-2.js"), +},{ +name: "icon-shopping-basket-alt", +path: () => import("./icons/icon-shopping-basket-alt.js"), +},{ +name: "icon-shopping-basket", +path: () => import("./icons/icon-shopping-basket.js"), +},{ +name: "icon-shuffle", +path: () => import("./icons/icon-shuffle.js"), +},{ +name: "icon-sience", +legacy: true, +hidden: true, +path: () => import("./icons/icon-sience.js"), +},{ +name: "icon-science", +path: () => import("./icons/icon-science.js"), +},{ +name: "icon-single-note", +path: () => import("./icons/icon-single-note.js"), +},{ +name: "icon-sitemap", +legacy: true, +hidden: true, +path: () => import("./icons/icon-sitemap.js"), +},{ +name: "icon-sleep", +path: () => import("./icons/icon-sleep.js"), +},{ +name: "icon-slideshow", +legacy: true, +hidden: true, +path: () => import("./icons/icon-slideshow.js"), +},{ +name: "icon-smiley-inverted", +legacy: true, +hidden: true, +path: () => import("./icons/icon-smiley-inverted.js"), +},{ +name: "icon-smiley", +path: () => import("./icons/icon-smiley.js"), +},{ +name: "icon-snow", +path: () => import("./icons/icon-snow.js"), +},{ +name: "icon-sound-low", +path: () => import("./icons/icon-sound-low.js"), +},{ +name: "icon-sound-medium", +legacy: true, +hidden: true, +path: () => import("./icons/icon-sound-medium.js"), +},{ +name: "icon-sound-off", +path: () => import("./icons/icon-sound-off.js"), +},{ +name: "icon-sound-waves", +path: () => import("./icons/icon-sound-waves.js"), +},{ +name: "icon-sound", +path: () => import("./icons/icon-sound.js"), +},{ +name: "icon-spades", +path: () => import("./icons/icon-spades.js"), +},{ +name: "icon-speaker", +path: () => import("./icons/icon-speaker.js"), +},{ +name: "icon-speed-gauge", +path: () => import("./icons/icon-speed-gauge.js"), +},{ +name: "icon-split-alt", +path: () => import("./icons/icon-split-alt.js"), +},{ +name: "icon-split", +path: () => import("./icons/icon-split.js"), +},{ +name: "icon-sprout", +path: () => import("./icons/icon-sprout.js"), +},{ +name: "icon-squiggly-line", +legacy: true, +hidden: true, +path: () => import("./icons/icon-squiggly-line.js"), +},{ +name: "icon-ssd", +legacy: true, +hidden: true, +path: () => import("./icons/icon-ssd.js"), +},{ +name: "icon-stacked-disks", +legacy: true, +hidden: true, +path: () => import("./icons/icon-stacked-disks.js"), +},{ +name: "icon-stamp", +legacy: true, +hidden: true, +path: () => import("./icons/icon-stamp.js"), +},{ +name: "icon-stop-alt", +path: () => import("./icons/icon-stop-alt.js"), +},{ +name: "icon-stop-hand", +legacy: true, +hidden: true, +path: () => import("./icons/icon-stop-hand.js"), +},{ +name: "icon-stop", +path: () => import("./icons/icon-stop.js"), +},{ +name: "icon-store", +path: () => import("./icons/icon-store.js"), +},{ +name: "icon-stream", +legacy: true, +hidden: true, +path: () => import("./icons/icon-stream.js"), +},{ +name: "icon-strikethrough", +path: () => import("./icons/icon-strikethrough.js"), +},{ +name: "icon-subscript", +path: () => import("./icons/icon-subscript.js"), +},{ +name: "icon-superscript", +path: () => import("./icons/icon-superscript.js"), +},{ +name: "icon-sunny", +path: () => import("./icons/icon-sunny.js"), +},{ +name: "icon-sweatshirt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-sweatshirt.js"), +},{ +name: "icon-sync", +path: () => import("./icons/icon-sync.js"), +},{ +name: "icon-t-shirt", +path: () => import("./icons/icon-t-shirt.js"), +},{ +name: "icon-tab-key", +path: () => import("./icons/icon-tab-key.js"), +},{ +name: "icon-table", +path: () => import("./icons/icon-table.js"), +},{ +name: "icon-tag", +path: () => import("./icons/icon-tag.js"), +},{ +name: "icon-tags", +path: () => import("./icons/icon-tags.js"), +},{ +name: "icon-takeaway-cup", +legacy: true, +hidden: true, +path: () => import("./icons/icon-takeaway-cup.js"), +},{ +name: "icon-target", +path: () => import("./icons/icon-target.js"), +},{ +name: "icon-temperatrure-alt", +path: () => import("./icons/icon-temperatrure-alt.js"), +},{ +name: "icon-temperature", +path: () => import("./icons/icon-temperature.js"), +},{ +name: "icon-terminal", +path: () => import("./icons/icon-terminal.js"), +},{ +name: "icon-text-align-center", +path: () => import("./icons/icon-text-align-center.js"), +},{ +name: "icon-text-align-justify", +path: () => import("./icons/icon-text-align-justify.js"), +},{ +name: "icon-text-align-left", +path: () => import("./icons/icon-text-align-left.js"), +},{ +name: "icon-text-align-right", +path: () => import("./icons/icon-text-align-right.js"), +},{ +name: "icon-text-direction-ltr", +path: () => import("./icons/icon-text-direction-ltr.js"), +},{ +name: "icon-text-direction-rtl", +path: () => import("./icons/icon-text-direction-rtl.js"), +},{ +name: "icon-theater", +path: () => import("./icons/icon-theater.js"), +},{ +name: "icon-thumb-down", +path: () => import("./icons/icon-thumb-down.js"), +},{ +name: "icon-thumb-up", +path: () => import("./icons/icon-thumb-up.js"), +},{ +name: "icon-thumbnail-list", +path: () => import("./icons/icon-thumbnail-list.js"), +},{ +name: "icon-thumbnails-small", +path: () => import("./icons/icon-thumbnails-small.js"), +},{ +name: "icon-thumbnails", +path: () => import("./icons/icon-thumbnails.js"), +},{ +name: "icon-ticket", +path: () => import("./icons/icon-ticket.js"), +},{ +name: "icon-time", +path: () => import("./icons/icon-time.js"), +},{ +name: "icon-timer", +path: () => import("./icons/icon-timer.js"), +},{ +name: "icon-tools", +legacy: true, +hidden: true, +path: () => import("./icons/icon-tools.js"), +},{ +name: "icon-top", +legacy: true, +hidden: true, +path: () => import("./icons/icon-top.js"), +},{ +name: "icon-traffic-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-traffic-alt.js"), +},{ +name: "icon-trafic", +path: () => import("./icons/icon-trafic.js"), +},{ +name: "icon-train", +path: () => import("./icons/icon-train.js"), +},{ +name: "icon-trash-alt-2", +legacy: true, +hidden: true, +path: () => import("./icons/icon-trash-alt-2.js"), +},{ +name: "icon-trash-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-trash-alt.js"), +},{ +name: "icon-trash", +path: () => import("./icons/icon-trash.js"), +},{ +name: "icon-tree", +path: () => import("./icons/icon-tree.js"), +},{ +name: "icon-trophy", +path: () => import("./icons/icon-trophy.js"), +},{ +name: "icon-truck", +path: () => import("./icons/icon-truck.js"), +},{ +name: "icon-tv-old", +path: () => import("./icons/icon-tv-old.js"), +},{ +name: "icon-tv", +path: () => import("./icons/icon-tv.js"), +},{ +name: "icon-umb-content", +legacy: true, +hidden: true, +path: () => import("./icons/icon-umb-content.js"), +},{ +name: "icon-umb-developer", +legacy: true, +hidden: true, +path: () => import("./icons/icon-umb-developer.js"), +},{ +name: "icon-umb-media", +legacy: true, +hidden: true, +path: () => import("./icons/icon-umb-media.js"), +},{ +name: "icon-umb-settings", +legacy: true, +hidden: true, +path: () => import("./icons/icon-umb-settings.js"), +},{ +name: "icon-umb-users", +legacy: true, +hidden: true, +path: () => import("./icons/icon-umb-users.js"), +},{ +name: "icon-umbrella", +path: () => import("./icons/icon-umbrella.js"), +},{ +name: "icon-undo", +path: () => import("./icons/icon-undo.js"), +},{ +name: "icon-underline", +path: () => import("./icons/icon-underline.js"), +},{ +name: "icon-unlink", +path: () => import("./icons/icon-unlink.js"), +},{ +name: "icon-unlocked", +path: () => import("./icons/icon-unlocked.js"), +},{ +name: "icon-unplug", +path: () => import("./icons/icon-unplug.js"), +},{ +name: "icon-untitled", +legacy: true, +hidden: true, +path: () => import("./icons/icon-untitled.js"), +},{ +name: "icon-usb-connector", +legacy: true, +hidden: true, +path: () => import("./icons/icon-usb-connector.js"), +},{ +name: "icon-usb", +path: () => import("./icons/icon-usb.js"), +},{ +name: "icon-user-female", +legacy: true, +hidden: true, +path: () => import("./icons/icon-user-female.js"), +},{ +name: "icon-user-females-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-user-females-alt.js"), +},{ +name: "icon-user-females", +legacy: true, +hidden: true, +path: () => import("./icons/icon-user-females.js"), +},{ +name: "icon-user-glasses", +legacy: true, +hidden: true, +path: () => import("./icons/icon-user-glasses.js"), +},{ +name: "icon-user", +path: () => import("./icons/icon-user.js"), +},{ +name: "icon-users-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-users-alt.js"), +},{ +name: "icon-users", +path: () => import("./icons/icon-users.js"), +},{ +name: "icon-utilities", +path: () => import("./icons/icon-utilities.js"), +},{ +name: "icon-vcard", +path: () => import("./icons/icon-vcard.js"), +},{ +name: "icon-video", +path: () => import("./icons/icon-video.js"), +},{ +name: "icon-voice", +path: () => import("./icons/icon-voice.js"), +},{ +name: "icon-wall-plug", +path: () => import("./icons/icon-wall-plug.js"), +},{ +name: "icon-wallet", +path: () => import("./icons/icon-wallet.js"), +},{ +name: "icon-wand", +path: () => import("./icons/icon-wand.js"), +},{ +name: "icon-webhook", +path: () => import("./icons/icon-webhook.js"), +},{ +name: "icon-weight", +path: () => import("./icons/icon-weight.js"), +},{ +name: "icon-width", +path: () => import("./icons/icon-width.js"), +},{ +name: "icon-wifi", +path: () => import("./icons/icon-wifi.js"), +},{ +name: "icon-window-popin", +path: () => import("./icons/icon-window-popin.js"), +},{ +name: "icon-window-popout", +path: () => import("./icons/icon-window-popout.js"), +},{ +name: "icon-window-sizes", +path: () => import("./icons/icon-window-sizes.js"), +},{ +name: "icon-wine-glass", +path: () => import("./icons/icon-wine-glass.js"), +},{ +name: "icon-wrench", +path: () => import("./icons/icon-wrench.js"), +},{ +name: "icon-wrong", +path: () => import("./icons/icon-wrong.js"), +},{ +name: "icon-zip", +path: () => import("./icons/icon-zip.js"), +},{ +name: "icon-zom-out", +legacy: true, +hidden: true, +path: () => import("./icons/icon-zom-out.js"), +},{ +name: "icon-zoom-in", +path: () => import("./icons/icon-zoom-in.js"), +},{ +name: "icon-zoom-out", +path: () => import("./icons/icon-zoom-out.js"), +},{ +name: "icon-star", +path: () => import("./icons/icon-star.js"), +},{ +name: "icon-database", +path: () => import("./icons/icon-database.js"), +},{ +name: "icon-umb-manifest", +hidden: true, +path: () => import("./icons/icon-umb-manifest.js"), +},{ +name: "icon-puzzle-piece", +path: () => import("./icons/icon-puzzle-piece.js"), +},{ +name: "icon-document-3d", +path: () => import("./icons/icon-document-3d.js"), +},{ +name: "icon-document-medal", +path: () => import("./icons/icon-document-medal.js"), +},{ +name: "icon-document-chart-bar", +path: () => import("./icons/icon-document-chart-bar.js"), +},{ +name: "icon-document-chart-graph", +path: () => import("./icons/icon-document-chart-graph.js"), +},{ +name: "icon-document-html", +path: () => import("./icons/icon-document-html.js"), +},{ +name: "icon-document-js", +path: () => import("./icons/icon-document-js.js"), +},{ +name: "icon-document-key", +path: () => import("./icons/icon-document-key.js"), +},{ +name: "icon-document-search", +path: () => import("./icons/icon-document-search.js"), +},{ +name: "icon-document-settings", +path: () => import("./icons/icon-document-settings.js"), +},{ +name: "icon-document-spreadsheet", +path: () => import("./icons/icon-document-spreadsheet.js"), +},{ +name: "icon-document-command", +path: () => import("./icons/icon-document-command.js"), +},{ +name: "icon-document-command", +path: () => import("./icons/icon-document-command.js"), +},{ +name: "icon-document-font", +path: () => import("./icons/icon-document-font.js"), +},{ +name: "icon-document-user", +path: () => import("./icons/icon-document-user.js"), +},{ +name: "icon-document-image", +path: () => import("./icons/icon-document-image.js"), +},{ +name: "icon-document-play", +path: () => import("./icons/icon-document-play.js"), +},{ +name: "icon-document-play", +path: () => import("./icons/icon-document-play.js"), +},{ +name: "icon-shared-value", +path: () => import("./icons/icon-shared-value.js"), +},{ +name: "icon-layout-masonry", +path: () => import("./icons/icon-layout-masonry.js"), +},{ +name: "icon-layout-grid", +path: () => import("./icons/icon-layout-grid.js"), +},{ +name: "icon-layout-list", +path: () => import("./icons/icon-layout-list.js"), +},{ +name: "icon-layout-panel-left", +path: () => import("./icons/icon-layout-panel-left.js"), +},{ +name: "icon-spray-can", +path: () => import("./icons/icon-spray-can.js"), +},{ +name: "icon-swatch-book", +path: () => import("./icons/icon-swatch-book.js"), +},{ +name: "icon-shape-cylinder", +path: () => import("./icons/icon-shape-cylinder.js"), +},{ +name: "icon-shape-triangle-right", +path: () => import("./icons/icon-shape-triangle-right.js"), +},{ +name: "icon-shape-triangle", +path: () => import("./icons/icon-shape-triangle.js"), +},{ +name: "icon-shape-circle", +path: () => import("./icons/icon-shape-circle.js"), +},{ +name: "icon-shape-square", +path: () => import("./icons/icon-shape-square.js"), +},{ +name: "icon-shape-hexagon", +path: () => import("./icons/icon-shape-hexagon.js"), +},{ +name: "icon-shape-rectangle-horizontal", +path: () => import("./icons/icon-shape-rectangle-horizontal.js"), +},{ +name: "icon-shape-rectangle-vertical", +path: () => import("./icons/icon-shape-rectangle-vertical.js"), +},{ +name: "icon-shapes", +path: () => import("./icons/icon-shapes.js"), +},{ +name: "icon-layout-dislocated", +path: () => import("./icons/icon-layout-dislocated.js"), +},{ +name: "icon-blend", +path: () => import("./icons/icon-blend.js"), +},{ +name: "icon-land-plot", +path: () => import("./icons/icon-land-plot.js"), +},{ +name: "icon-facebook", +path: () => import("./icons/icon-facebook.js"), +},{ +name: "icon-gitbook", +path: () => import("./icons/icon-gitbook.js"), +},{ +name: "icon-github", +path: () => import("./icons/icon-github.js"), +},{ +name: "icon-gitlab", +path: () => import("./icons/icon-gitlab.js"), +},{ +name: "icon-google", +path: () => import("./icons/icon-google.js"), +},{ +name: "icon-mastodon", +path: () => import("./icons/icon-mastodon.js"), +},{ +name: "icon-twitter-x", +path: () => import("./icons/icon-twitter-x.js"), +},{ +name: "icon-art-easel", +legacy: true, +hidden: true, +path: () => import("./icons/icon-art-easel.js"), +},{ +name: "icon-article", +legacy: true, +hidden: true, +path: () => import("./icons/icon-article.js"), +},{ +name: "icon-auction-hammer", +legacy: true, +hidden: true, +path: () => import("./icons/icon-auction-hammer.js"), +},{ +name: "icon-badge-count", +legacy: true, +hidden: true, +path: () => import("./icons/icon-badge-count.js"), +},{ +name: "icon-band-aid", +legacy: true, +hidden: true, +path: () => import("./icons/icon-band-aid.js"), +},{ +name: "icon-baby-stroller", +legacy: true, +hidden: true, +path: () => import("./icons/icon-baby-stroller.js"), +},{ +name: "icon-bill-dollar", +legacy: true, +hidden: true, +path: () => import("./icons/icon-bill-dollar.js"), +},{ +name: "icon-bill-euro", +legacy: true, +hidden: true, +path: () => import("./icons/icon-bill-euro.js"), +},{ +name: "icon-bill-pound", +legacy: true, +hidden: true, +path: () => import("./icons/icon-bill-pound.js"), +},{ +name: "icon-bill-yen", +legacy: true, +hidden: true, +path: () => import("./icons/icon-bill-yen.js"), +},{ +name: "icon-bill", +legacy: true, +hidden: true, +path: () => import("./icons/icon-bill.js"), +},{ +name: "icon-billboard", +legacy: true, +hidden: true, +path: () => import("./icons/icon-billboard.js"), +},{ +name: "icon-bills-dollar", +legacy: true, +hidden: true, +path: () => import("./icons/icon-bills-dollar.js"), +},{ +name: "icon-bills-euro", +legacy: true, +hidden: true, +path: () => import("./icons/icon-bills-euro.js"), +},{ +name: "icon-bills-pound", +legacy: true, +hidden: true, +path: () => import("./icons/icon-bills-pound.js"), +},{ +name: "icon-bills-yen", +legacy: true, +hidden: true, +path: () => import("./icons/icon-bills-yen.js"), +},{ +name: "icon-bills", +legacy: true, +hidden: true, +path: () => import("./icons/icon-bills.js"), +},{ +name: "icon-blueprint", +legacy: true, +hidden: true, +path: () => import("./icons/icon-blueprint.js"), +},{ +name: "icon-bomb", +legacy: true, +hidden: true, +path: () => import("./icons/icon-bomb.js"), +},{ +name: "icon-cash-register", +legacy: true, +hidden: true, +path: () => import("./icons/icon-cash-register.js"), +},{ +name: "icon-checkbox-dotted-active", +legacy: true, +hidden: true, +path: () => import("./icons/icon-checkbox-dotted-active.js"), +},{ +name: "icon-chess", +legacy: true, +hidden: true, +path: () => import("./icons/icon-chess.js"), +},{ +name: "icon-circus", +legacy: true, +hidden: true, +path: () => import("./icons/icon-circus.js"), +},{ +name: "icon-clothes-hanger", +legacy: true, +hidden: true, +path: () => import("./icons/icon-clothes-hanger.js"), +},{ +name: "icon-coin", +legacy: true, +hidden: true, +path: () => import("./icons/icon-coin.js"), +},{ +name: "icon-coins-dollar-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-coins-dollar-alt.js"), +},{ +name: "icon-coins-dollar", +legacy: true, +hidden: true, +path: () => import("./icons/icon-coins-dollar.js"), +},{ +name: "icon-coins-euro-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-coins-euro-alt.js"), +},{ +name: "icon-coins-euro", +legacy: true, +hidden: true, +path: () => import("./icons/icon-coins-euro.js"), +},{ +name: "icon-coins-pound-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-coins-pound-alt.js"), +},{ +name: "icon-coins-pound", +legacy: true, +hidden: true, +path: () => import("./icons/icon-coins-pound.js"), +},{ +name: "icon-coins-yen-alt", +legacy: true, +hidden: true, +path: () => import("./icons/icon-coins-yen-alt.js"), +},{ +name: "icon-coins-yen", +legacy: true, +hidden: true, +path: () => import("./icons/icon-coins-yen.js"), +},{ +name: "icon-comb", +legacy: true, +hidden: true, +path: () => import("./icons/icon-comb.js"), +},{ +name: "icon-desk", +legacy: true, +hidden: true, +path: () => import("./icons/icon-desk.js"), +},{ +name: "icon-dollar-bag", +legacy: true, +hidden: true, +path: () => import("./icons/icon-dollar-bag.js"), +},{ +name: "icon-eject", +legacy: true, +hidden: true, +path: () => import("./icons/icon-eject.js"), +},{ +name: "icon-euro-bag", +legacy: true, +hidden: true, +path: () => import("./icons/icon-euro-bag.js"), +},{ +name: "icon-female-symbol", +legacy: true, +hidden: true, +path: () => import("./icons/icon-female-symbol.js"), +},{ +name: "icon-firewall", +legacy: true, +hidden: true, +path: () => import("./icons/icon-firewall.js"), +},{ +name: "icon-folder-open", +legacy: true, +hidden: true, +path: () => import("./icons/icon-folder-open.js"), +},{ +name: "icon-folder-outline", +legacy: true, +hidden: true, +path: () => import("./icons/icon-folder-outline.js"), +},{ +name: "icon-handprint", +legacy: true, +hidden: true, +path: () => import("./icons/icon-handprint.js"), +},{ +name: "icon-hat", +legacy: true, +hidden: true, +path: () => import("./icons/icon-hat.js"), +},{ +name: "icon-hd", +legacy: true, +hidden: true, +path: () => import("./icons/icon-hd.js"), +},{ +name: "icon-inactive-line", +legacy: true, +hidden: true, +path: () => import("./icons/icon-inactive-line.js"), +},{ +name: "icon-keychain", +legacy: true, +hidden: true, +path: () => import("./icons/icon-keychain.js"), +},{ +name: "icon-keyhole", +legacy: true, +hidden: true, +path: () => import("./icons/icon-keyhole.js"), +},{ +name: "icon-linkedin", +legacy: true, +hidden: true, +path: () => import("./icons/icon-linkedin.js"), +},{ +name: "icon-linux-tux", +legacy: true, +hidden: true, +path: () => import("./icons/icon-linux-tux.js"), +},{ +name: "icon-male-and-female", +legacy: true, +hidden: true, +path: () => import("./icons/icon-male-and-female.js"), +},{ +name: "icon-male-symbol", +legacy: true, +hidden: true, +path: () => import("./icons/icon-male-symbol.js"), +},{ +name: "icon-molecular-network", +legacy: true, +hidden: true, +path: () => import("./icons/icon-molecular-network.js"), +},{ +name: "icon-molecular", +legacy: true, +hidden: true, +path: () => import("./icons/icon-molecular.js"), +},{ +name: "icon-umbraco", +path: () => import("./icons/icon-umbraco.js"), +},{ +name: "icon-azure", +legacy: true, +hidden: true, +path: () => import("./icons/icon-azure.js"), +},{ +name: "icon-microsoft", +legacy: true, +hidden: true, +path: () => import("./icons/icon-microsoft.js"), +},{ +name: "icon-os-x", +legacy: true, +hidden: true, +path: () => import("./icons/icon-os-x.js"), +},{ +name: "icon-pants", +legacy: true, +hidden: true, +path: () => import("./icons/icon-pants.js"), +},{ +name: "icon-parachute-drop", +legacy: true, +hidden: true, +path: () => import("./icons/icon-parachute-drop.js"), +},{ +name: "icon-parental-control", +legacy: true, +hidden: true, +path: () => import("./icons/icon-parental-control.js"), +},{ +name: "icon-path", +legacy: true, +hidden: true, +path: () => import("./icons/icon-path.js"), +},{ +name: "icon-piracy", +legacy: true, +hidden: true, +path: () => import("./icons/icon-piracy.js"), +},{ +name: "icon-poker-chip", +legacy: true, +hidden: true, +path: () => import("./icons/icon-poker-chip.js"), +},{ +name: "icon-pound-bag", +legacy: true, +hidden: true, +path: () => import("./icons/icon-pound-bag.js"), +},{ +name: "icon-receipt-dollar", +legacy: true, +hidden: true, +path: () => import("./icons/icon-receipt-dollar.js"), +},{ +name: "icon-receipt-euro", +legacy: true, +hidden: true, +path: () => import("./icons/icon-receipt-euro.js"), +},{ +name: "icon-receipt-pound", +legacy: true, +hidden: true, +path: () => import("./icons/icon-receipt-pound.js"), +},{ +name: "icon-receipt-yen", +legacy: true, +hidden: true, +path: () => import("./icons/icon-receipt-yen.js"), +},{ +name: "icon-road", +legacy: true, +hidden: true, +path: () => import("./icons/icon-road.js"), +},{ +name: "icon-safe", +legacy: true, +hidden: true, +path: () => import("./icons/icon-safe.js"), +},{ +name: "icon-safedial", +legacy: true, +hidden: true, +path: () => import("./icons/icon-safedial.js"), +},{ +name: "icon-sandbox-toys", +legacy: true, +hidden: true, +path: () => import("./icons/icon-sandbox-toys.js"), +},{ +name: "icon-security-camera", +legacy: true, +hidden: true, +path: () => import("./icons/icon-security-camera.js"), +},{ +name: "icon-settings-alt-2", +legacy: true, +hidden: true, +path: () => import("./icons/icon-settings-alt-2.js"), +},{ +name: "icon-share-alt-2", +legacy: true, +hidden: true, +path: () => import("./icons/icon-share-alt-2.js"), +},{ +name: "icon-shorts", +legacy: true, +hidden: true, +path: () => import("./icons/icon-shorts.js"), +},{ +name: "icon-simcard", +legacy: true, +hidden: true, +path: () => import("./icons/icon-simcard.js"), +},{ +name: "icon-tab", +legacy: true, +hidden: true, +path: () => import("./icons/icon-tab.js"), +},{ +name: "icon-tactics", +legacy: true, +hidden: true, +path: () => import("./icons/icon-tactics.js"), +},{ +name: "icon-theif", +legacy: true, +hidden: true, +path: () => import("./icons/icon-theif.js"), +},{ +name: "icon-thought-bubble", +legacy: true, +hidden: true, +path: () => import("./icons/icon-thought-bubble.js"), +},{ +name: "icon-twitter", +legacy: true, +hidden: true, +path: () => import("./icons/icon-twitter.js"), +},{ +name: "icon-umb-contour", +legacy: true, +hidden: true, +path: () => import("./icons/icon-umb-contour.js"), +},{ +name: "icon-umb-deploy", +legacy: true, +hidden: true, +path: () => import("./icons/icon-umb-deploy.js"), +},{ +name: "icon-umb-members", +legacy: true, +hidden: true, +path: () => import("./icons/icon-umb-members.js"), +},{ +name: "icon-universal", +legacy: true, +hidden: true, +path: () => import("./icons/icon-universal.js"), +},{ +name: "icon-war", +legacy: true, +hidden: true, +path: () => import("./icons/icon-war.js"), +},{ +name: "icon-windows", +legacy: true, +hidden: true, +path: () => import("./icons/icon-windows.js"), +},{ +name: "icon-yen-bag", +legacy: true, +hidden: true, +path: () => import("./icons/icon-yen-bag.js"), +}]; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-azure.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-azure.ts index dbb4c71663..a9fa4a7733 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-azure.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-azure.ts @@ -1 +1 @@ -export default ``; \ No newline at end of file +export default `Microsoft Azure`; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-facebook.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-facebook.ts index 3a694dd65c..d50f7ef036 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-facebook.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-facebook.ts @@ -1 +1 @@ -export default ``; \ No newline at end of file +export default `Facebook`; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-gitbook.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-gitbook.ts index 76f5e769fb..c098a56070 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-gitbook.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-gitbook.ts @@ -1 +1 @@ -export default ``; \ No newline at end of file +export default `GitBook`; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-github.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-github.ts index c9a0ca4670..84ca5c77da 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-github.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-github.ts @@ -1 +1 @@ -export default ``; \ No newline at end of file +export default `GitHub`; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-gitlab.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-gitlab.ts index 34de202aa0..8e86fe12a0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-gitlab.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-gitlab.ts @@ -1 +1 @@ -export default ``; \ No newline at end of file +export default `GitLab`; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-google.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-google.ts index 84e344b7b3..4c7fdec581 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-google.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-google.ts @@ -1 +1 @@ -export default ``; \ No newline at end of file +export default `Google`; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-mastodon.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-mastodon.ts index 9a44442b4f..6e33bb6c68 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-mastodon.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-mastodon.ts @@ -1 +1 @@ -export default ``; \ No newline at end of file +export default `Mastodon`; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-microsoft.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-microsoft.ts index 0659a2f186..bcef0848b1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-microsoft.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-microsoft.ts @@ -1 +1 @@ -export default ``; \ No newline at end of file +export default `Microsoft`; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-pictures.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-pictures.ts index c89d1c844b..7bd63c45ff 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-pictures.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-pictures.ts @@ -1 +1 @@ -export default ``; \ No newline at end of file +export default ``; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-twitter-x.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-twitter-x.ts index 1376c7d65b..1f457a8568 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-twitter-x.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-twitter-x.ts @@ -1 +1 @@ -export default ``; \ No newline at end of file +export default `X`; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-twitter.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-twitter.ts index 400c012ae9..854aca4e38 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-twitter.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-twitter.ts @@ -1 +1 @@ -export default ``; \ No newline at end of file +export default `Twitter`; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-umbrella.ts b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-umbrella.ts index a6eb9d6fed..8238221d0a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-umbrella.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/icon-registry/icons/icon-umbrella.ts @@ -1 +1 @@ -export default ``; \ No newline at end of file +export default ``; \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/action/action-menu-item.api.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/action/action-menu-item.api.ts index fc355cb98e..ea8daf3344 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/action/action-menu-item.api.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/action/action-menu-item.api.ts @@ -5,6 +5,11 @@ export abstract class UmbMenuItemActionApiBase extends UmbActionBase> implements UmbMenuItemActionApi { + /** + * By specifying the `execute` method, the action will act as a button. + * @abstract + * @returns {Promise} + */ public execute(): Promise { return Promise.resolve(); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/action/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/action/types.ts index 6e60d0bfdd..1da233a9bd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/action/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/action/types.ts @@ -1,4 +1,4 @@ -import type { MetaMenuItem } from '../../../menu-item.extension.js'; +import type { MetaMenuItem } from '../menu-item.extension.js'; import type { ManifestElementAndApi, ManifestWithDynamicConditions } from '@umbraco-cms/backoffice/extension-api'; import type { UmbAction } from '@umbraco-cms/backoffice/action'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/constants.ts new file mode 100644 index 0000000000..a86e19fbb3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/constants.ts @@ -0,0 +1 @@ +export * from './menu-item.context.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/expansion/is-menu-item-expansion-entry.guard.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/expansion/is-menu-item-expansion-entry.guard.ts new file mode 100644 index 0000000000..84c6d2441e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/expansion/is-menu-item-expansion-entry.guard.ts @@ -0,0 +1,16 @@ +import type { UmbMenuItemExpansionEntryModel } from '../../types.js'; + +/** + * Checks if the provided object is a Menu Item Expansion Entry. + * @param {object } object - The object to check. + * @returns {boolean } True if the object is a Menu Item Expansion Entry, false otherwise. + */ +export function isMenuItemExpansionEntry(object: unknown): object is UmbMenuItemExpansionEntryModel { + return ( + typeof object === 'object' && + object !== null && + 'entityType' in object && + 'unique' in object && + 'menuItemAlias' in object + ); +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/expansion/menu-item-expansion.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/expansion/menu-item-expansion.manager.ts new file mode 100644 index 0000000000..5b96983adb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/expansion/menu-item-expansion.manager.ts @@ -0,0 +1,132 @@ +import type { UmbMenuItemExpansionEntryModel } from '../../menu/types.js'; +import { UMB_MENU_CONTEXT } from '../../menu/constants.js'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { + createObservablePart, + type UmbObserverController, + type Observable, +} from '@umbraco-cms/backoffice/observable-api'; +import { UmbEntityExpansionManager, type UmbEntityExpansionModel } from '@umbraco-cms/backoffice/utils'; + +/** + * Manages the expansion state of a menu item + * @exports + * @class UmbMenuItemExpansionManager + * @augments {UmbControllerBase} + */ +export class UmbMenuItemExpansionManager extends UmbControllerBase { + #manager = new UmbEntityExpansionManager(this); + public readonly expansion = this.#manager.expansion; + + #menuItemAlias?: string; + #menuContext?: typeof UMB_MENU_CONTEXT.TYPE; + #observerController?: UmbObserverController; + + constructor(host: UmbControllerHost) { + super(host); + + this.consumeContext(UMB_MENU_CONTEXT, (menuContext) => { + this.#menuContext = menuContext; + this.#observeMenuExpansion(); + }); + } + + #observeMenuExpansion() { + if (!this.#menuContext || !this.#menuItemAlias) { + this.#observerController?.destroy(); + return; + } + + this.#observerController = this.observe( + createObservablePart( + this.#menuContext.expansion.expansion, + (items: Array) => + items?.filter((item) => item.menuItemAlias === this.#menuItemAlias) || [], + ), + (itemsForMenuItem) => { + this.#manager.setExpansion(itemsForMenuItem); + }, + 'observeMenuExpension', + ); + } + + setMenuItemAlias(alias: string | undefined): void { + this.#menuItemAlias = alias; + this.#observeMenuExpansion(); + } + + /** + * Checks if an entry is expanded + * @param {UmbMenuItemExpansionEntryModel} entry The entry to check + * @returns {Observable} True if the entry is expanded + * @memberof UmbSectionSidebarMenuSectionExpansionManager + */ + isExpanded(entry: UmbMenuItemExpansionEntryModel): Observable { + return this.#manager.isExpanded(entry); + } + + /** + * Sets the expansion state + * @param {UmbEntityExpansionModel | undefined} expansion The expansion state + * @memberof UmbSectionSidebarMenuSectionExpansionManager + * @returns {void} + */ + setExpansion(expansion: UmbEntityExpansionModel): void { + this.#manager.setExpansion(expansion); + this.#menuContext?.expansion.setExpansion(expansion); + } + + /** + * Gets the expansion state + * @memberof UmbSectionSidebarMenuSectionExpansionManager + * @returns {UmbEntityExpansionModel} The expansion state + */ + getExpansion(): UmbEntityExpansionModel { + return this.#manager.getExpansion(); + } + + /** + * Opens a menu item + * @param {UmbMenuItemExpansionEntryModel} entry The item to open + * @memberof UmbSectionSidebarMenuSectionExpansionManager + * @returns {Promise} + */ + public async expandItem(entry: UmbMenuItemExpansionEntryModel): Promise { + this.#manager.expandItem(entry); + this.#menuContext?.expansion.expandItem(entry); + } + + /** + * Expands multiple entities + * @param {UmbEntityExpansionModel} entries The entities to open + * @memberof UmbEntityExpansionManager + * @returns {void} + */ + public expandItems(entries: UmbEntityExpansionModel): void { + this.#manager.expandItems(entries); + this.#menuContext?.expansion.expandItems(entries); + } + + /** + * Closes a menu item + * @param {UmbMenuItemExpansionEntryModel} entry The item to close + * @memberof UmbSectionSidebarMenuSectionExpansionManager + * @returns {Promise} + */ + public async collapseItem(entry: UmbMenuItemExpansionEntryModel): Promise { + this.#manager.collapseItem(entry); + this.#menuContext?.expansion.collapseItem(entry); + } + + /** + * Closes all menu items + * @memberof UmbSectionSidebarMenuSectionExpansionManager + * @returns {Promise} + */ + public async collapseAll(): Promise { + const localEntries = this.#manager.getExpansion(); + this.#manager.collapseAll(); + this.#menuContext?.expansion.collapseItems(localEntries); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/index.ts index 6b2eaef349..80ecd73a78 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/index.ts @@ -1,2 +1,3 @@ export * from './menu-item-default.element.js'; +export * from './constants.js'; export * from './action/action-menu-item.api.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/link/link-menu-item.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/link/link-menu-item.element.ts index b902cd18e2..0aeab0024f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/link/link-menu-item.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/link/link-menu-item.element.ts @@ -1,4 +1,4 @@ -import type { UmbMenuItemElement } from '../../../menu-item-element.interface.js'; +import type { UmbMenuItemElement } from '../menu-item-element.interface.js'; import type { ManifestMenuItemLinkKind } from './types.js'; import { customElement, html, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/link/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/link/types.ts index 2bc68ac163..f5be9cefbe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/link/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/link/types.ts @@ -1,4 +1,4 @@ -import type { ManifestMenuItem, MetaMenuItem } from '../../../menu-item.extension.js'; +import type { ManifestMenuItem, MetaMenuItem } from '../menu-item.extension.js'; export interface ManifestMenuItemLinkKind extends ManifestMenuItem { type: 'menuItem'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/menu-item-default.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/menu-item-default.element.ts index 269133145c..035c5f972c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/menu-item-default.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/menu-item-default.element.ts @@ -1,5 +1,5 @@ -import type { UmbMenuItemElement } from '../../menu-item-element.interface.js'; -import type { ManifestMenuItem } from '../../menu-item.extension.js'; +import type { UmbMenuItemElement } from './menu-item-element.interface.js'; +import type { ManifestMenuItem } from './menu-item.extension.js'; import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UMB_SECTION_CONTEXT } from '@umbraco-cms/backoffice/section'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/menu-item-element.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/menu-item-element.interface.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/menu/menu-item-element.interface.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/menu-item-element.interface.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/menu-item.context.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/menu-item.context.token.ts new file mode 100644 index 0000000000..f51e5f1042 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/menu-item.context.token.ts @@ -0,0 +1,4 @@ +import type { UmbDefaultMenuItemContext } from './menu-item.context.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export const UMB_MENU_ITEM_CONTEXT = new UmbContextToken('UmbMenuItemContext'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/menu-item.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/menu-item.context.ts new file mode 100644 index 0000000000..d20e137b1b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/menu-item.context.ts @@ -0,0 +1,22 @@ +import type { ManifestMenuItem } from '../../types.js'; +import { UmbMenuItemExpansionManager } from './expansion/menu-item-expansion.manager.js'; +import { UMB_MENU_ITEM_CONTEXT } from './menu-item.context.token.js'; +import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export class UmbDefaultMenuItemContext extends UmbContextBase { + public readonly expansion = new UmbMenuItemExpansionManager(this); + + #manifest?: ManifestMenuItem | undefined; + public get manifest(): ManifestMenuItem | undefined { + return this.#manifest; + } + public set manifest(value: ManifestMenuItem | undefined) { + this.#manifest = value; + this.expansion.setMenuItemAlias(value?.alias); + } + + constructor(host: UmbControllerHost) { + super(host, UMB_MENU_ITEM_CONTEXT); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/menu-item.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/menu-item.extension.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/menu/menu-item.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/menu-item.extension.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/types.ts index 1e39b8bf82..ed3511d796 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu-item/types.ts @@ -1,2 +1,4 @@ export type * from './action/types.js'; export type * from './link/types.js'; +export type * from './menu-item-element.interface.js'; +export type * from './menu-item.extension.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/constants.ts new file mode 100644 index 0000000000..95d903ca23 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/constants.ts @@ -0,0 +1 @@ +export * from './menu.context.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/index.ts index 242a263f7a..be0be98f97 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/index.ts @@ -1 +1,2 @@ export * from './menu.element.js'; +export * from './constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/menu.context.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/menu.context.token.ts new file mode 100644 index 0000000000..6385f34578 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/menu.context.token.ts @@ -0,0 +1,4 @@ +import type { UmbDefaultMenuContext } from './menu.context.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export const UMB_MENU_CONTEXT = new UmbContextToken('UmbMenuContext'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/menu.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/menu.context.ts new file mode 100644 index 0000000000..671373df7c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/menu.context.ts @@ -0,0 +1,13 @@ +import { UMB_MENU_CONTEXT } from './menu.context.token.js'; +import type { UmbMenuItemExpansionEntryModel } from './types.js'; +import { UmbEntityExpansionManager } from '@umbraco-cms/backoffice/utils'; +import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export class UmbDefaultMenuContext extends UmbContextBase { + public readonly expansion = new UmbEntityExpansionManager(this); + + constructor(host: UmbControllerHost) { + super(host, UMB_MENU_CONTEXT); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/menu.element.ts index 7e051db0e0..9ed2c2d95d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/menu.element.ts @@ -1,7 +1,11 @@ import type { ManifestMenu } from '../../menu.extension.js'; -import type { ManifestMenuItem } from '../../menu-item.extension.js'; +import { UmbDefaultMenuItemContext } from '../menu-item/menu-item.context.js'; +import type { ManifestMenuItem } from '../menu-item/types.js'; +import { UmbDefaultMenuContext } from './menu.context.js'; +import type { UmbMenuItemExpansionEntryModel } from './types.js'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import type { UmbEntityExpansionModel } from '@umbraco-cms/backoffice/utils'; import '../menu-item/menu-item-default.element.js'; @@ -10,13 +14,24 @@ export class UmbMenuElement extends UmbLitElement { @property({ attribute: false }) manifest?: ManifestMenu; + public get expansion(): UmbEntityExpansionModel { + return this.#context.expansion.getExpansion(); + } + public set expansion(value: UmbEntityExpansionModel) { + this.#context.expansion.setExpansion(value); + } + + #context = new UmbDefaultMenuContext(this); + override render() { + //TODO: We should probably have resolved the MENU ITEM CONTEXT not by providing it not as an API, currently this work cause menu items does not have APIs as of their manifest. But if they do this, this would be a problem. [NL] return html` - items.meta.menus.includes(this.manifest!.alias)}> - + `; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/types.ts new file mode 100644 index 0000000000..dc3d827be2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/menu/types.ts @@ -0,0 +1,5 @@ +import type { UmbEntityExpansionEntryModel } from '@umbraco-cms/backoffice/utils'; + +export interface UmbMenuItemExpansionEntryModel extends UmbEntityExpansionEntryModel { + menuItemAlias: string; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/types.ts index c858b14791..e03ad31735 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/components/types.ts @@ -1 +1,2 @@ export type * from './menu-item/types.js'; +export type * from './menu/types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/constants.ts new file mode 100644 index 0000000000..fd7b96c0f8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/constants.ts @@ -0,0 +1 @@ +export * from './section-sidebar-menu/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/index.ts index c7d6c36f9e..cce4aa408e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/index.ts @@ -3,6 +3,7 @@ export * from './menu-tree-structure-workspace-context-base.js'; export * from './menu-structure-workspace-context.context-token.js'; export * from './menu-variant-structure-workspace-context.context-token.js'; export * from './menu-variant-tree-structure-workspace-context-base.js'; +export * from './constants.js'; export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/manifests.ts index a9d3417c61..350fea31bc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/manifests.ts @@ -1,8 +1,12 @@ -import { manifests as menuItemManifests } from './components/menu-item/manifests.js'; import { manifest as menuAliasConditionManifest } from './conditions/menu-alias.condition.js'; +import { manifests as menuItemManifests } from './components/menu-item/manifests.js'; +import { manifests as sectionSidebarMenuManifests } from './section-sidebar-menu/manifests.js'; +import { manifests as sectionSidebarMenuWithEntityActionsManifests } from './section-sidebar-menu-with-entity-actions/manifests.js'; import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; export const manifests: Array = [ ...menuItemManifests, + ...sectionSidebarMenuManifests, + ...sectionSidebarMenuWithEntityActionsManifests, menuAliasConditionManifest, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/menu-structure/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/menu-structure/types.ts new file mode 100644 index 0000000000..08be58ee57 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/menu-structure/types.ts @@ -0,0 +1,17 @@ +import type { ManifestWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; + +export interface ManifestWorkspaceContextMenuStructureKind extends ManifestWorkspaceContext { + type: 'workspaceContext'; + kind: 'menuStructure'; + meta: MetaWorkspaceContextMenuStructureKind; +} + +export interface MetaWorkspaceContextMenuStructureKind { + menuItemAlias: string; +} + +declare global { + interface UmbExtensionManifestMap { + umbManifestWorkspaceContextMenuStructureKind: ManifestWorkspaceContextMenuStructureKind; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/menu-tree-structure-workspace-context-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/menu-tree-structure-workspace-context-base.ts index 21df3dcb27..fabaaccd80 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/menu-tree-structure-workspace-context-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/menu-tree-structure-workspace-context-base.ts @@ -1,5 +1,6 @@ -import type { UmbStructureItemModel } from './types.js'; +import type { ManifestWorkspaceContextMenuStructureKind, UmbStructureItemModel } from './types.js'; import { UMB_MENU_STRUCTURE_WORKSPACE_CONTEXT } from './menu-structure-workspace-context.context-token.js'; +import { UMB_SECTION_SIDEBAR_MENU_SECTION_CONTEXT } from './section-sidebar-menu/index.js'; import type { UmbTreeRepository, UmbTreeItemModel, UmbTreeRootModel } from '@umbraco-cms/backoffice/tree'; import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; @@ -7,6 +8,8 @@ import { UMB_SUBMITTABLE_TREE_ENTITY_WORKSPACE_CONTEXT } from '@umbraco-cms/back import { UmbArrayState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbAncestorsEntityContext, UmbParentEntityContext, type UmbEntityModel } from '@umbraco-cms/backoffice/entity'; +import { linkEntityExpansionEntries } from '@umbraco-cms/backoffice/utils'; +import { UMB_MODAL_CONTEXT } from '@umbraco-cms/backoffice/modal'; interface UmbMenuTreeStructureWorkspaceContextBaseArgs { treeRepositoryAlias: string; @@ -14,6 +17,8 @@ interface UmbMenuTreeStructureWorkspaceContextBaseArgs { // TODO: introduce base class for all menu structure workspaces to handle ancestors and parent export abstract class UmbMenuTreeStructureWorkspaceContextBase extends UmbContextBase { + manifest?: ManifestWorkspaceContextMenuStructureKind; + #workspaceContext?: typeof UMB_SUBMITTABLE_TREE_ENTITY_WORKSPACE_CONTEXT.TYPE; #args: UmbMenuTreeStructureWorkspaceContextBaseArgs; @@ -28,6 +33,8 @@ export abstract class UmbMenuTreeStructureWorkspaceContextBase extends UmbContex #parentContext = new UmbParentEntityContext(this); #ancestorContext = new UmbAncestorsEntityContext(this); + #sectionSidebarMenuContext?: typeof UMB_SECTION_SIDEBAR_MENU_SECTION_CONTEXT.TYPE; + #isModalContext: boolean = false; constructor(host: UmbControllerHost, args: UmbMenuTreeStructureWorkspaceContextBaseArgs) { super(host, UMB_MENU_STRUCTURE_WORKSPACE_CONTEXT); @@ -35,18 +42,45 @@ export abstract class UmbMenuTreeStructureWorkspaceContextBase extends UmbContex this.provideContext('UmbMenuStructureWorkspaceContext', this); this.#args = args; + this.consumeContext(UMB_MODAL_CONTEXT, (modalContext) => { + this.#isModalContext = modalContext !== undefined; + }); + + this.consumeContext(UMB_SECTION_SIDEBAR_MENU_SECTION_CONTEXT, (instance) => { + this.#sectionSidebarMenuContext = instance; + }); + this.consumeContext(UMB_SUBMITTABLE_TREE_ENTITY_WORKSPACE_CONTEXT, (instance) => { this.#workspaceContext = instance; this.observe(this.#workspaceContext?.unique, (value) => { if (!value) return; this.#requestStructure(); }); + + this.observe(this.#workspaceContext?.isNew, (value) => { + if (value === undefined) return; + this.#requestStructure(); + }); }); } async #requestStructure() { + const isNew = this.#workspaceContext?.getIsNew(); + const uniqueObservable = isNew + ? this.#workspaceContext?._internal_createUnderParentEntityUnique + : this.#workspaceContext?.unique; + const entityTypeObservable = isNew + ? this.#workspaceContext?._internal_createUnderParentEntityType + : this.#workspaceContext?.entityType; + let structureItems: Array = []; + const unique = (await this.observe(uniqueObservable, () => {})?.asPromise()) as string; + if (unique === undefined) throw new Error('Unique is not available'); + + const entityType = (await this.observe(entityTypeObservable, () => {})?.asPromise()) as string; + if (!entityType) throw new Error('Entity type is not available'); + const treeRepository = await createExtensionApiByAlias>( this, this.#args.treeRepositoryAlias, @@ -65,22 +99,10 @@ export abstract class UmbMenuTreeStructureWorkspaceContextBase extends UmbContex ]; } - const isNew = this.#workspaceContext?.getIsNew(); - - const entityTypeObservable = isNew - ? this.#workspaceContext?._internal_createUnderParentEntityType - : this.#workspaceContext?.entityType; - const entityType = (await this.observe(entityTypeObservable, () => {})?.asPromise()) as string; - if (!entityType) throw new Error('Entity type is not available'); + const isRoot = entityType === root?.entityType; // If the entity type is different from the root entity type, then we can request the ancestors. - if (entityType !== root?.entityType) { - const uniqueObservable = isNew - ? this.#workspaceContext?._internal_createUnderParentEntityUnique - : this.#workspaceContext?.unique; - const unique = (await this.observe(uniqueObservable, () => {})?.asPromise()) as string; - if (!unique) throw new Error('Unique is not available'); - + if (!isRoot) { const { data } = await treeRepository.requestTreeItemAncestors({ treeItem: { unique, entityType } }); if (data) { @@ -93,13 +115,19 @@ export abstract class UmbMenuTreeStructureWorkspaceContextBase extends UmbContex }; }); - structureItems.push(...ancestorItems); - - this.#structure.setValue(structureItems); - this.#setParentData(structureItems); this.#setAncestorData(data); + + structureItems.push(...ancestorItems); } } + + this.#structure.setValue(structureItems); + this.#setParentData(structureItems); + + const menuItemAlias = this.manifest?.meta?.menuItemAlias; + if (menuItemAlias && !this.#isModalContext) { + this.#expandSectionSidebarMenu(structureItems, menuItemAlias); + } } #setParentData(structureItems: Array) { @@ -139,4 +167,25 @@ export abstract class UmbMenuTreeStructureWorkspaceContextBase extends UmbContex this.#ancestorContext.setAncestors(ancestorEntities); } + + #expandSectionSidebarMenu(structureItems: Array, menuItemAlias: string) { + const linkedEntries = linkEntityExpansionEntries(structureItems); + // Filter out the current entity as we don't want to expand it + const expandableItems = linkedEntries.filter((item) => item.unique !== this.#workspaceContext?.getUnique()); + const expandableItemsWithMenuItem = expandableItems.map((item) => { + return { + ...item, + menuItemAlias, + }; + }); + this.#sectionSidebarMenuContext?.expansion.expandItems(expandableItemsWithMenuItem); + } + + override destroy(): void { + super.destroy(); + this.#structure.destroy(); + this.#parent.destroy(); + this.#parentContext.destroy(); + this.#ancestorContext.destroy(); + } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/menu-variant-tree-structure-workspace-context-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/menu-variant-tree-structure-workspace-context-base.ts index db207951b0..3ef07138e2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/menu-variant-tree-structure-workspace-context-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/menu-variant-tree-structure-workspace-context-base.ts @@ -1,5 +1,6 @@ -import type { UmbVariantStructureItemModel } from './types.js'; +import type { ManifestWorkspaceContextMenuStructureKind, UmbVariantStructureItemModel } from './types.js'; import { UMB_MENU_VARIANT_STRUCTURE_WORKSPACE_CONTEXT } from './menu-variant-structure-workspace-context.context-token.js'; +import { UMB_SECTION_SIDEBAR_MENU_SECTION_CONTEXT } from './section-sidebar-menu/section-context/section-sidebar-menu.section-context.token.js'; import type { UmbTreeItemModel, UmbTreeRepository, UmbTreeRootModel } from '@umbraco-cms/backoffice/tree'; import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; @@ -7,6 +8,8 @@ import { UmbArrayState, UmbObjectState } from '@umbraco-cms/backoffice/observabl import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbAncestorsEntityContext, UmbParentEntityContext, type UmbEntityModel } from '@umbraco-cms/backoffice/entity'; import { UMB_SUBMITTABLE_TREE_ENTITY_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; +import { linkEntityExpansionEntries } from '@umbraco-cms/backoffice/utils'; +import { UMB_MODAL_CONTEXT } from '@umbraco-cms/backoffice/modal'; interface UmbMenuVariantTreeStructureWorkspaceContextBaseArgs { treeRepositoryAlias: string; @@ -14,7 +17,8 @@ interface UmbMenuVariantTreeStructureWorkspaceContextBaseArgs { // TODO: introduce base class for all menu structure workspaces to handle ancestors and parent export abstract class UmbMenuVariantTreeStructureWorkspaceContextBase extends UmbContextBase { - // + manifest?: ManifestWorkspaceContextMenuStructureKind; + #workspaceContext?: typeof UMB_SUBMITTABLE_TREE_ENTITY_WORKSPACE_CONTEXT.TYPE; #args: UmbMenuVariantTreeStructureWorkspaceContextBaseArgs; @@ -29,6 +33,8 @@ export abstract class UmbMenuVariantTreeStructureWorkspaceContextBase extends Um #parentContext = new UmbParentEntityContext(this); #ancestorContext = new UmbAncestorsEntityContext(this); + #sectionSidebarMenuContext?: typeof UMB_SECTION_SIDEBAR_MENU_SECTION_CONTEXT.TYPE; + #isModalContext: boolean = false; public readonly IS_MENU_VARIANT_STRUCTURE_WORKSPACE_CONTEXT = true; @@ -38,6 +44,14 @@ export abstract class UmbMenuVariantTreeStructureWorkspaceContextBase extends Um this.provideContext('UmbMenuStructureWorkspaceContext', this); this.#args = args; + this.consumeContext(UMB_MODAL_CONTEXT, (modalContext) => { + this.#isModalContext = modalContext !== undefined; + }); + + this.consumeContext(UMB_SECTION_SIDEBAR_MENU_SECTION_CONTEXT, (instance) => { + this.#sectionSidebarMenuContext = instance; + }); + this.consumeContext(UMB_SUBMITTABLE_TREE_ENTITY_WORKSPACE_CONTEXT, (instance) => { this.#workspaceContext = instance; this.observe( @@ -108,6 +122,11 @@ export abstract class UmbMenuVariantTreeStructureWorkspaceContextBase extends Um this.#structure.setValue(structureItems); this.#setParentData(structureItems); this.#setAncestorData(data); + + const menuItemAlias = this.manifest?.meta?.menuItemAlias; + if (menuItemAlias && !this.#isModalContext) { + this.#expandSectionSidebarMenu(structureItems, menuItemAlias); + } } } @@ -148,4 +167,25 @@ export abstract class UmbMenuVariantTreeStructureWorkspaceContextBase extends Um this.#ancestorContext.setAncestors(ancestorEntities); } + + #expandSectionSidebarMenu(structureItems: Array, menuItemAlias: string) { + const linkedEntries = linkEntityExpansionEntries(structureItems); + // Filter out the current entity as we don't want to expand it + const expandableItems = linkedEntries.filter((item) => item.unique !== this.#workspaceContext?.getUnique()); + const expandableItemsWithMenuItem = expandableItems.map((item) => { + return { + ...item, + menuItemAlias, + }; + }); + this.#sectionSidebarMenuContext?.expansion.expandItems(expandableItemsWithMenuItem); + } + + override destroy(): void { + super.destroy(); + this.#structure.destroy(); + this.#parent.destroy(); + this.#parentContext.destroy(); + this.#ancestorContext.destroy(); + } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu-with-entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu-with-entity-actions/manifests.ts new file mode 100644 index 0000000000..4d7249e76c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu-with-entity-actions/manifests.ts @@ -0,0 +1,14 @@ +import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [ + { + type: 'kind', + alias: 'Umb.Kind.SectionSidebarAppMenuWithEntityActions', + matchKind: 'menuWithEntityActions', + matchType: 'sectionSidebarApp', + manifest: { + type: 'sectionSidebarApp', + element: () => import('./section-sidebar-menu-with-entity-actions.element.js'), + }, + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu-with-entity-actions/section-sidebar-menu-with-entity-actions.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu-with-entity-actions/section-sidebar-menu-with-entity-actions.element.ts index adce591646..f7c885fc10 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu-with-entity-actions/section-sidebar-menu-with-entity-actions.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu-with-entity-actions/section-sidebar-menu-with-entity-actions.element.ts @@ -1,22 +1,8 @@ import { UmbSectionSidebarMenuElement } from '../section-sidebar-menu/section-sidebar-menu.element.js'; import type { ManifestSectionSidebarAppMenuWithEntityActionsKind } from '../section-sidebar-menu/types.js'; import { css, html, customElement, type PropertyValues, state } from '@umbraco-cms/backoffice/external/lit'; -import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { UmbParentEntityContext } from '@umbraco-cms/backoffice/entity'; -const manifestWithEntityActions: UmbExtensionManifestKind = { - type: 'kind', - alias: 'Umb.Kind.SectionSidebarAppMenuWithEntityActions', - matchKind: 'menuWithEntityActions', - matchType: 'sectionSidebarApp', - manifest: { - type: 'sectionSidebarApp', - elementName: 'umb-section-sidebar-menu-with-entity-actions', - }, -}; -umbExtensionsRegistry.register(manifestWithEntityActions); - @customElement('umb-section-sidebar-menu-with-entity-actions') export class UmbSectionSidebarMenuWithEntityActionsElement extends UmbSectionSidebarMenuElement { @state() diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/constants.ts new file mode 100644 index 0000000000..125299d13a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/constants.ts @@ -0,0 +1,2 @@ +export * from './global-context/constants.js'; +export * from './section-context/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/global-context/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/global-context/constants.ts new file mode 100644 index 0000000000..b559697700 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/global-context/constants.ts @@ -0,0 +1 @@ +export * from './section-sidebar-menu.global-context.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/global-context/expansion/section-sidebar-menu-app-expansion.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/global-context/expansion/section-sidebar-menu-app-expansion.manager.ts new file mode 100644 index 0000000000..9e58ecca59 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/global-context/expansion/section-sidebar-menu-app-expansion.manager.ts @@ -0,0 +1,23 @@ +import type { UmbSectionMenuItemExpansionEntryModel } from '../../types.js'; +import type { Observable } from '@umbraco-cms/backoffice/observable-api'; +import { UmbEntityExpansionManager } from '@umbraco-cms/backoffice/utils'; + +/** + * Manages the expansion state of a section sidebar menu. + * @exports + * @class UmbSectionSidebarMenuAppExpansionManager + * @augments {UmbControllerBase} + */ +export class UmbSectionSidebarMenuAppExpansionManager extends UmbEntityExpansionManager { + /** + * Returns an observable of the expansion state filtered by section alias. + * @param {string} sectionAlias The alias of the section to filter by. + * @returns {Observable} An observable of the expansion state for the specified section alias. + * @memberof UmbSectionSidebarMenuAppExpansionManager + */ + expansionBySectionAlias(sectionAlias: string): Observable { + return this._expansion.asObservablePart((entries) => + entries.filter((entry) => entry.sectionAlias === sectionAlias), + ); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/global-context/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/global-context/manifests.ts new file mode 100644 index 0000000000..1812e1d5f6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/global-context/manifests.ts @@ -0,0 +1,10 @@ +import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [ + { + type: 'globalContext', + alias: 'Umb.GlobalContext.SectionSidebarMenu', + name: 'Section Sidebar Menu Global Context', + api: () => import('./section-sidebar-menu.global-context.js'), + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/global-context/section-sidebar-menu.global-context.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/global-context/section-sidebar-menu.global-context.token.ts new file mode 100644 index 0000000000..20c8eb36fa --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/global-context/section-sidebar-menu.global-context.token.ts @@ -0,0 +1,6 @@ +import type { UmbSectionSidebarMenuGlobalContext } from './section-sidebar-menu.global-context.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export const UMB_SECTION_SIDEBAR_MENU_GLOBAL_CONTEXT = new UmbContextToken( + 'UmbSectionSidebarMenuGlobalContext', +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/global-context/section-sidebar-menu.global-context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/global-context/section-sidebar-menu.global-context.ts new file mode 100644 index 0000000000..afcd007487 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/global-context/section-sidebar-menu.global-context.ts @@ -0,0 +1,14 @@ +import { UMB_SECTION_SIDEBAR_MENU_GLOBAL_CONTEXT } from './section-sidebar-menu.global-context.token.js'; +import { UmbSectionSidebarMenuAppExpansionManager } from './expansion/section-sidebar-menu-app-expansion.manager.js'; +import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export class UmbSectionSidebarMenuGlobalContext extends UmbContextBase { + public readonly expansion = new UmbSectionSidebarMenuAppExpansionManager(this); + + constructor(host: UmbControllerHost) { + super(host, UMB_SECTION_SIDEBAR_MENU_GLOBAL_CONTEXT); + } +} + +export { UmbSectionSidebarMenuGlobalContext as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/index.ts index 2d3149a8d4..0371f2072e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/index.ts @@ -1 +1,2 @@ export * from './section-sidebar-menu.element.js'; +export * from './constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/manifests.ts new file mode 100644 index 0000000000..b04a4c1661 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/manifests.ts @@ -0,0 +1,18 @@ +import { manifests as sectionContextManifests } from './section-context/manifests.js'; +import { manifests as globalContextManifests } from './global-context/manifests.js'; +import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [ + { + type: 'kind', + alias: 'Umb.Kind.SectionSidebarAppMenu', + matchKind: 'menu', + matchType: 'sectionSidebarApp', + manifest: { + type: 'sectionSidebarApp', + element: () => import('./section-sidebar-menu.element.js'), + }, + }, + ...sectionContextManifests, + ...globalContextManifests, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-context/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-context/constants.ts new file mode 100644 index 0000000000..75f40d61ad --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-context/constants.ts @@ -0,0 +1 @@ +export * from './section-sidebar-menu.section-context.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-context/expansion/section-sidebar-menu-section-expansion.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-context/expansion/section-sidebar-menu-section-expansion.manager.ts new file mode 100644 index 0000000000..162861d5b8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-context/expansion/section-sidebar-menu-section-expansion.manager.ts @@ -0,0 +1,139 @@ +import { UMB_SECTION_SIDEBAR_MENU_GLOBAL_CONTEXT } from '../../global-context/section-sidebar-menu.global-context.token.js'; +import type { UmbSectionMenuItemExpansionEntryModel } from '../../types.js'; +import type { UmbMenuItemExpansionEntryModel } from '../../../components/menu/types.js'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { Observable } from '@umbraco-cms/backoffice/observable-api'; +import { UMB_SECTION_CONTEXT } from '@umbraco-cms/backoffice/section'; +import { UmbEntityExpansionManager, type UmbEntityExpansionModel } from '@umbraco-cms/backoffice/utils'; + +/** + * Manages the expansion state of a section sidebar menu. + * @exports + * @class UmbSectionSidebarMenuSectionExpansionManager + * @augments {UmbControllerBase} + */ +export class UmbSectionSidebarMenuSectionExpansionManager extends UmbControllerBase { + #manager = new UmbEntityExpansionManager(this); + public readonly expansion = this.#manager.expansion; + + #sectionContext?: typeof UMB_SECTION_CONTEXT.TYPE; + #globalContext?: typeof UMB_SECTION_SIDEBAR_MENU_GLOBAL_CONTEXT.TYPE; + #currentSectionAlias?: string; + + constructor(host: UmbControllerHost) { + super(host); + + this.consumeContext(UMB_SECTION_CONTEXT, (sectionContext) => { + this.#sectionContext = sectionContext; + this.#observeCurrentSectionAlias(); + }); + + this.consumeContext(UMB_SECTION_SIDEBAR_MENU_GLOBAL_CONTEXT, (globalContext) => { + this.#globalContext = globalContext; + this.#observeGlobalMenuExpansion(); + }); + } + + #observeCurrentSectionAlias() { + this.observe(this.#sectionContext?.alias, (alias) => { + if (!alias) return; + this.#currentSectionAlias = alias; + }); + } + + #observeGlobalMenuExpansion() { + if (!this.#globalContext || !this.#currentSectionAlias) return; + this.observe(this.#globalContext?.expansion.expansionBySectionAlias(this.#currentSectionAlias), (expansion) => { + this.#manager.setExpansion(expansion); + }); + } + + /** + * Checks if an entry is expanded + * @param {UmbMenuItemExpansionEntryModel} entry The entry to check + * @returns {Observable} True if the entry is expanded + * @memberof UmbSectionSidebarMenuSectionExpansionManager + */ + isExpanded(entry: UmbMenuItemExpansionEntryModel): Observable { + return this.#manager.isExpanded(entry); + } + + /** + * Sets the expansion state + * @param {UmbEntityExpansionModel | undefined} expansion The expansion state + * @memberof UmbSectionSidebarMenuSectionExpansionManager + * @returns {void} + */ + setExpansion(expansion: UmbEntityExpansionModel): void { + this.#manager.setExpansion(expansion); + const entries = this.#bindEntriesToSection(expansion); + this.#globalContext?.expansion.setExpansion(entries); + } + + /** + * Gets the expansion state + * @memberof UmbSectionSidebarMenuSectionExpansionManager + * @returns {UmbEntityExpansionModel} The expansion state + */ + getExpansion(): UmbEntityExpansionModel { + return this.#manager.getExpansion(); + } + + /** + * Opens a menu item + * @param {UmbMenuItemExpansionEntryModel} entry The item to open + * @memberof UmbSectionSidebarMenuSectionExpansionManager + * @returns {Promise} + */ + public async expandItem(entry: UmbMenuItemExpansionEntryModel): Promise { + this.#manager.expandItem(entry); + const entries = this.#bindEntriesToSection([entry]); + this.#globalContext?.expansion.expandItem(entries[0]); + } + + /** + * Expands multiple entities + * @param {UmbEntityExpansionModel} entries The entities to open + * @memberof UmbEntityExpansionManager + * @returns {void} + */ + public expandItems(entries: UmbEntityExpansionModel): void { + this.#manager.expandItems(entries); + const entriesWithSectionAlias = this.#bindEntriesToSection(entries); + this.#globalContext?.expansion.expandItems(entriesWithSectionAlias); + } + + /** + * Closes a menu item + * @param {UmbMenuItemExpansionEntryModel} entry The item to close + * @memberof UmbSectionSidebarMenuSectionExpansionManager + * @returns {Promise} + */ + public async collapseItem(entry: UmbMenuItemExpansionEntryModel): Promise { + this.#manager.collapseItem(entry); + const entries = this.#bindEntriesToSection([entry]); + this.#globalContext?.expansion.collapseItem(entries[0]); + } + + /** + * Closes all menu items + * @memberof UmbSectionSidebarMenuSectionExpansionManager + * @returns {Promise} + */ + public async collapseAll(): Promise { + // Collapse all items in the global context matching the current section + const entries = this.#bindEntriesToSection(this.#manager.getExpansion()); + this.#manager.collapseAll(); + this.#globalContext?.expansion.collapseItems(entries); + } + + #bindEntriesToSection( + expansion: UmbEntityExpansionModel, + ): UmbEntityExpansionModel { + return expansion.map((item) => ({ + ...item, + sectionAlias: this.#currentSectionAlias, + })); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-context/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-context/manifests.ts new file mode 100644 index 0000000000..19c4552e6d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-context/manifests.ts @@ -0,0 +1,10 @@ +import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [ + { + type: 'sectionContext', + alias: 'Umb.SectionContext.SectionSidebarMenu', + name: 'Section Sidebar Menu Section Context', + api: () => import('./section-sidebar-menu.section-context.js'), + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-context/section-sidebar-menu.section-context.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-context/section-sidebar-menu.section-context.token.ts new file mode 100644 index 0000000000..d0ae6e6eba --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-context/section-sidebar-menu.section-context.token.ts @@ -0,0 +1,8 @@ +import type { UmbSectionSidebarMenuSectionContext } from './section-sidebar-menu.section-context.js'; +import { UMB_SECTION_CONTEXT } from '@umbraco-cms/backoffice/section'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export const UMB_SECTION_SIDEBAR_MENU_SECTION_CONTEXT = new UmbContextToken( + UMB_SECTION_CONTEXT.contextAlias, + 'UmbSectionSidebarMenuSectionContext', +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-context/section-sidebar-menu.section-context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-context/section-sidebar-menu.section-context.ts new file mode 100644 index 0000000000..e4cfe3861f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-context/section-sidebar-menu.section-context.ts @@ -0,0 +1,14 @@ +import { UmbSectionSidebarMenuSectionExpansionManager } from './expansion/section-sidebar-menu-section-expansion.manager.js'; +import { UMB_SECTION_SIDEBAR_MENU_SECTION_CONTEXT } from './section-sidebar-menu.section-context.token.js'; +import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export class UmbSectionSidebarMenuSectionContext extends UmbContextBase { + public readonly expansion = new UmbSectionSidebarMenuSectionExpansionManager(this); + + constructor(host: UmbControllerHost) { + super(host, UMB_SECTION_SIDEBAR_MENU_SECTION_CONTEXT); + } +} + +export { UmbSectionSidebarMenuSectionContext as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-sidebar-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-sidebar-menu.element.ts index 51b8b8c34d..fc50249783 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-sidebar-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/section-sidebar-menu.element.ts @@ -1,23 +1,12 @@ import type { ManifestMenu } from '../menu.extension.js'; +import { isMenuItemExpansionEntry } from '../components/menu-item/expansion/is-menu-item-expansion-entry.guard.js'; import type { ManifestSectionSidebarAppBaseMenu, ManifestSectionSidebarAppMenuKind } from './types.js'; +import { UMB_SECTION_SIDEBAR_MENU_SECTION_CONTEXT } from './section-context/section-sidebar-menu.section-context.token.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; -import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; - -// TODO: Move to separate file: -const manifest: UmbExtensionManifestKind = { - type: 'kind', - alias: 'Umb.Kind.SectionSidebarAppMenu', - matchKind: 'menu', - matchType: 'sectionSidebarApp', - manifest: { - type: 'sectionSidebarApp', - elementName: 'umb-section-sidebar-menu', - }, -}; -umbExtensionsRegistry.register(manifest); +import { UmbExpansionEntryCollapsedEvent, UmbExpansionEntryExpandedEvent } from '@umbraco-cms/backoffice/utils'; +import { UmbExtensionSlotElement } from '@umbraco-cms/backoffice/extension-registry'; @customElement('umb-section-sidebar-menu') export class UmbSectionSidebarMenuElement< @@ -34,14 +23,73 @@ export class UmbSectionSidebarMenuElement< return html`

${this.localize.string(this.manifest?.meta?.label ?? '')}

`; } + #sectionSidebarMenuContext?: typeof UMB_SECTION_SIDEBAR_MENU_SECTION_CONTEXT.TYPE; + + #extensionSlotElement = new UmbExtensionSlotElement(); + #muteStateUpdate = false; + + constructor() { + super(); + this.#initExtensionSlotElement(); + this.consumeContext(UMB_SECTION_SIDEBAR_MENU_SECTION_CONTEXT, (context) => { + this.#sectionSidebarMenuContext = context; + this.#observeExpansion(); + }); + } + + #initExtensionSlotElement() { + /* For better performance and UX we prevent lit from doing unnecessary rerenders we programmatically create the element, + and manually update the props when needed. */ + + this.#extensionSlotElement.type = 'menu'; + this.#extensionSlotElement.filter = (menu: ManifestMenu) => menu.alias === this.manifest?.meta?.menu; + this.#extensionSlotElement.defaultElement = 'umb-menu'; + this.#extensionSlotElement.events = { + [UmbExpansionEntryExpandedEvent.TYPE]: this.#onExpansionChange.bind(this), + [UmbExpansionEntryCollapsedEvent.TYPE]: this.#onExpansionChange.bind(this), + }; + } + + #observeExpansion() { + this.observe(this.#sectionSidebarMenuContext?.expansion.expansion, (items) => { + if (this.#muteStateUpdate) return; + + this.#extensionSlotElement.props = { + expansion: items || [], + }; + }); + } + + #onExpansionChange(e: Event) { + const event = e as UmbExpansionEntryExpandedEvent | UmbExpansionEntryCollapsedEvent; + event.stopPropagation(); + const eventEntry = event.entry; + + if (!eventEntry) { + throw new Error('Entity is required to toggle expansion.'); + } + + // Only react to the event if it is a valid Menu Item Expansion Entry + if (isMenuItemExpansionEntry(eventEntry) === false) return; + + if (event.type === UmbExpansionEntryExpandedEvent.TYPE) { + this.#muteStateUpdate = true; + this.#sectionSidebarMenuContext?.expansion.expandItem(eventEntry); + this.#muteStateUpdate = false; + } else if (event.type === UmbExpansionEntryCollapsedEvent.TYPE) { + this.#muteStateUpdate = true; + this.#sectionSidebarMenuContext?.expansion.collapseItem(eventEntry); + this.#muteStateUpdate = false; + } + } + override render() { - return html` - ${this.renderHeader()} - - `; + return html` ${this.renderHeader()} ${this.#extensionSlotElement}`; + } + + override disconnectedCallback(): void { + super.disconnectedCallback(); + this.#extensionSlotElement.destroy(); } static override styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/types.ts index af00746daf..85a1cf64ab 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/section-sidebar-menu/types.ts @@ -1,3 +1,4 @@ +import type { UmbMenuItemExpansionEntryModel } from '../components/menu/types.js'; import type { ManifestSectionSidebarApp } from '@umbraco-cms/backoffice/section'; export interface MetaSectionSidebarAppMenuKind { @@ -24,3 +25,7 @@ export interface ManifestSectionSidebarAppMenuWithEntityActionsKind extends Mani export interface MetaSectionSidebarAppMenuWithEntityActionsKind extends MetaSectionSidebarAppMenuKind { entityType: string; } + +export interface UmbSectionMenuItemExpansionEntryModel extends UmbMenuItemExpansionEntryModel { + sectionAlias?: string; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/menu/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/menu/types.ts index 93dda2eb92..028299b81a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/menu/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/menu/types.ts @@ -2,9 +2,9 @@ import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; export type * from './components/types.js'; export type * from './conditions/types.js'; -export type * from './menu-item-element.interface.js'; -export type * from './menu-item.extension.js'; +export type * from './menu-structure/types.js'; export type * from './menu.extension.js'; +export type * from './section-sidebar-menu/types.js'; // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface UmbStructureItemModelBase extends UmbEntityModel {} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/openapi-ts.config.js b/src/Umbraco.Web.UI.Client/src/packages/core/openapi-ts.config.js index 41ee9c4301..db0a583eb4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/openapi-ts.config.js +++ b/src/Umbraco.Web.UI.Client/src/packages/core/openapi-ts.config.js @@ -1,7 +1,6 @@ import { defineConfig } from '@hey-api/openapi-ts'; export default defineConfig({ - debug: true, input: '../../../../Umbraco.Cms.Api.Management/OpenApi.json', output: { path: './backend-api', @@ -9,7 +8,6 @@ export default defineConfig({ plugins: [ { name: '@hey-api/client-fetch', - bundle: false, exportFromIndex: true, throwOnError: true, }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/package.json b/src/Umbraco.Web.UI.Client/src/packages/core/package.json index d4907d423e..e9ab5ca0c9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/package.json +++ b/src/Umbraco.Web.UI.Client/src/packages/core/package.json @@ -10,9 +10,11 @@ "@types/diff": "^7.0.2", "diff": "^7.0.0", "uuid": "^11.1.0", - "@hey-api/client-fetch": "^0.12.0" + "@hey-api/openapi-ts": "0.81.1" }, "devDependencies": { - "@hey-api/openapi-ts": "^0.71.0" + "lucide-static": "^0.542.0", + "simple-icons": "^15.13.0", + "svgo": "^4.0.0" } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/index.ts index dc09da0afb..84193ff39d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/index.ts @@ -1,5 +1,6 @@ export * from './components/index.js'; export * from './conditions/index.js'; +export * from './property-dataset-property-validator/index.js'; export * from './property-dataset/index.js'; export * from './property-guard-manager/index.js'; export * from './property-value-cloner/property-value-clone.controller.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property-dataset-property-validator/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property-dataset-property-validator/index.ts new file mode 100644 index 0000000000..101f710e39 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property-dataset-property-validator/index.ts @@ -0,0 +1 @@ +export * from './property-dataset-property-validator.controller.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property-dataset-property-validator/property-dataset-property-validator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property-dataset-property-validator/property-dataset-property-validator.controller.ts new file mode 100644 index 0000000000..be18b6cb79 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property-dataset-property-validator/property-dataset-property-validator.controller.ts @@ -0,0 +1,28 @@ +import { UMB_PROPERTY_DATASET_CONTEXT } from '../property-dataset/property-dataset-context.token.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbValueValidator, type UmbValueValidatorArgs } from '@umbraco-cms/backoffice/validation'; + +export interface UmbPropertyDatasetPropertyValidatorArgs extends UmbValueValidatorArgs { + propertyAlias: string; +} + +// The Example Workspace Context Controller: +export class UmbPropertyDatasetPropertyValidator extends UmbValueValidator { + // + #propertyAlias: string; + + constructor(host: UmbControllerHost, args: UmbPropertyDatasetPropertyValidatorArgs) { + super(host, args); + this.#propertyAlias = args.propertyAlias; + + this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, async (context) => { + this.observe( + await context?.propertyValueByAlias(this.#propertyAlias), + (value) => { + this.value = value; + }, + 'observeDatasetValue', + ); + }); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts index b5a0dc6c38..35436b03e0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/api-interceptor.controller.ts @@ -5,7 +5,7 @@ import type { UmbProblemDetails } from './types.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth'; import type { UmbNotificationColor } from '@umbraco-cms/backoffice/notification'; -import type { RequestOptions, umbHttpClient } from '@umbraco-cms/backoffice/http-client'; +import type { umbHttpClient } from '@umbraco-cms/backoffice/http-client'; const MAX_RETRIES = 3; @@ -16,7 +16,7 @@ export class UmbApiInterceptorController extends UmbControllerBase { */ #pending401Requests: Array<{ request: Request; - requestConfig: RequestOptions; + requestConfig: unknown; retry: () => Promise; resolve: (value: Response) => void; reject: (reason?: unknown) => void; @@ -29,7 +29,7 @@ export class UmbApiInterceptorController extends UmbControllerBase { * These requests will not be retried, as they are not idempotent. * Instead, we will notify the user about these requests after re-authentication. */ - #nonGet401Requests: Array<{ request: Request; requestConfig: RequestOptions }> = []; + #nonGet401Requests: Array<{ request: Request; requestConfig: unknown }> = []; /** * Binds the default interceptors to the client. diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-alias/section-alias.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-alias/section-alias.condition.ts index f18f3e75f6..91dc47b6bf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-alias/section-alias.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-alias/section-alias.condition.ts @@ -1,8 +1,8 @@ -import { UmbConditionBase } from '../../../extension-registry/conditions/condition-base.controller.js'; -import { UMB_SECTION_CONTEXT } from '../../section.context.js'; import type { SectionAliasConditionConfig } from '../types.js'; +import { UMB_SECTION_CONTEXT } from '../../section.context.token.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbConditionControllerArguments, UmbExtensionCondition } from '@umbraco-cms/backoffice/extension-api'; +import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry'; export class UmbSectionAliasCondition extends UmbConditionBase diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/default/default-section.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/default/default-section.context.ts new file mode 100644 index 0000000000..26378d4914 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/default/default-section.context.ts @@ -0,0 +1,8 @@ +import { UmbSectionContext } from '../section.context.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export class UmbDefaultSectionContext extends UmbSectionContext { + constructor(host: UmbControllerHost) { + super(host); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/section-default.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/default/default-section.element.ts similarity index 84% rename from src/Umbraco.Web.UI.Client/src/packages/core/section/section-default.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/section/default/default-section.element.ts index 9b3d6fc293..aed2293213 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/section-default.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/default/default-section.element.ts @@ -1,6 +1,7 @@ -import type { ManifestSectionRoute } from './extensions/section-route.extension.js'; -import type { UmbSectionMainViewElement } from './section-main-views/section-main-views.element.js'; -import type { ManifestSection, UmbSectionElement } from './types.js'; +import type { ManifestSectionRoute } from '../extensions/section-route.extension.js'; +import type { UmbSectionMainViewElement } from '../section-main-views/section-main-views.element.js'; +import type { ManifestSection, UmbSectionElement } from '../types.js'; +import { UmbDefaultSectionContext } from './default-section.context.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, nothing, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; @@ -21,7 +22,7 @@ import { UMB_MARK_ATTRIBUTE_NAME } from '@umbraco-cms/backoffice/const'; * @description - Element hosting sections and section navigation. */ @customElement('umb-section-default') -export class UmbSectionDefaultElement extends UmbLitElement implements UmbSectionElement { +export class UmbDefaultSectionElement extends UmbLitElement implements UmbSectionElement { private _manifest?: ManifestSection | undefined; @property({ type: Object, attribute: false }) @@ -32,7 +33,7 @@ export class UmbSectionDefaultElement extends UmbLitElement implements UmbSectio const oldValue = this._manifest; if (oldValue === value) return; this._manifest = value; - + this.#api.setManifest(value); this.requestUpdate('manifest', oldValue); } @@ -45,6 +46,9 @@ export class UmbSectionDefaultElement extends UmbLitElement implements UmbSectio @state() private _splitPanelPosition = '300px'; + // TODO: v17: Move this to a manifest api. It will have to wait for a major as it will be a breaking change. + #api = new UmbDefaultSectionContext(this); + constructor() { super(); @@ -111,7 +115,7 @@ export class UmbSectionDefaultElement extends UmbLitElement implements UmbSectio ...routes, { path: '**', - component: () => import('./section-main-views/section-main-views.element.js'), + component: () => import('../section-main-views/section-main-views.element.js'), setup: (element) => { (element as UmbSectionMainViewElement).sectionAlias = this.manifest?.alias; }, @@ -177,8 +181,16 @@ export class UmbSectionDefaultElement extends UmbLitElement implements UmbSectio ]; } +export { UmbDefaultSectionElement as element }; + declare global { interface HTMLElementTagNameMap { - 'umb-section-default': UmbSectionDefaultElement; + 'umb-section-default': UmbDefaultSectionElement; } } + +/** + * + * @deprecated Since 16. Use UmbDefaultSectionElement instead. UmbSectionDefaultElement will be removed in v18. + */ +export { UmbDefaultSectionElement as UmbSectionDefaultElement }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/default/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/default/manifests.ts new file mode 100644 index 0000000000..1802584f0f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/default/manifests.ts @@ -0,0 +1,20 @@ +import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [ + { + type: 'kind', + alias: 'Umb.Kind.Section.Default', + matchKind: 'default', + matchType: 'section', + manifest: { + type: 'section', + kind: 'default', + weight: 1000, + element: () => import('./default-section.element.js'), + meta: { + label: '', + pathname: '', + }, + }, + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/default/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/default/types.ts new file mode 100644 index 0000000000..17eeb41ff0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/default/types.ts @@ -0,0 +1,11 @@ +import type { ManifestSection } from '../../section/extensions/index.js'; + +export interface ManifestSectionDefaultKind extends ManifestSection { + kind: 'default'; +} + +declare global { + interface UmbExtensionManifestMap { + umbDefaultSectionKind: ManifestSectionDefaultKind; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/extensions/section-context.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/extensions/section-context.extension.ts new file mode 100644 index 0000000000..25e0e114cc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/extensions/section-context.extension.ts @@ -0,0 +1,17 @@ +import type { ManifestApi, ManifestWithDynamicConditions, UmbApi } from '@umbraco-cms/backoffice/extension-api'; + +export interface ManifestSectionContext + extends ManifestWithDynamicConditions, + ManifestApi { + type: 'sectionContext'; + meta: MetaType; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +export interface MetaSectionContext {} + +declare global { + interface UmbExtensionManifestMap { + UmbManifestSectionContext: ManifestSectionContext; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/extensions/section.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/extensions/section.extension.ts index 63f363013b..b3d90397f5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/extensions/section.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/extensions/section.extension.ts @@ -2,6 +2,7 @@ import type { UmbSectionElement } from './section-element.interface.js'; import type { ManifestElement, ManifestWithDynamicConditions } from '@umbraco-cms/backoffice/extension-api'; export interface ManifestSection + // TODO: v17 change to extend Element and Api extends ManifestElement, ManifestWithDynamicConditions { type: 'section'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/extensions/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/extensions/types.ts index e4d5de7f8b..9e65c93996 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/extensions/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/extensions/types.ts @@ -1,3 +1,4 @@ +export type * from './section-context.extension.js'; export type * from './section-element.interface.js'; export type * from './section-sidebar-app-element.interface.js'; export type * from './section-view-element.interface.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/index.ts index 9ac642e834..03fa9cfd53 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/index.ts @@ -1,11 +1,10 @@ export * from './components/index.js'; export * from './constants.js'; -export * from './section-default.element.js'; +export * from './default/default-section.element.js'; export * from './section-main/index.js'; export * from './section-picker-modal/section-picker-modal.token.js'; export * from './section-sidebar-context-menu/index.js'; -export * from '../menu/section-sidebar-menu-with-entity-actions/index.js'; -export * from '../menu/section-sidebar-menu/index.js'; export * from './section-sidebar/index.js'; export * from './section.context.js'; +export * from './section.context.token.js'; export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/section-main/section-main.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/section-main/section-main.element.ts index 144a2b2704..ebae159f82 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/section-main/section-main.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/section-main/section-main.element.ts @@ -1,8 +1,9 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { css, html, LitElement, customElement } from '@umbraco-cms/backoffice/external/lit'; +import { css, html, customElement } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; @customElement('umb-section-main') -export class UmbSectionMainElement extends LitElement { +export class UmbSectionMainElement extends UmbLitElement { override render() { return html`
diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/section.context.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/section.context.token.ts new file mode 100644 index 0000000000..b70b7b5ebf --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/section.context.token.ts @@ -0,0 +1,4 @@ +import type { UmbSectionContext } from './section.context.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export const UMB_SECTION_CONTEXT = new UmbContextToken('UmbSectionContext'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/section.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/section.context.ts index fbb626897f..510697e146 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/section.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/section.context.ts @@ -1,8 +1,10 @@ import type { ManifestSection } from './extensions/section.extension.js'; +import { UMB_SECTION_CONTEXT } from './section.context.token.js'; import { UmbStringState } from '@umbraco-cms/backoffice/observable-api'; -import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; +import { UmbExtensionsApiInitializer } from '@umbraco-cms/backoffice/extension-api'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; export class UmbSectionContext extends UmbContextBase { #manifestAlias = new UmbStringState(undefined); @@ -12,8 +14,11 @@ export class UmbSectionContext extends UmbContextBase { public readonly pathname = this.#manifestPathname.asObservable(); public readonly label = this.#manifestLabel.asObservable(); + #sectionContextExtensionController?: UmbExtensionsApiInitializer; + constructor(host: UmbControllerHost) { super(host, UMB_SECTION_CONTEXT); + this.#createSectionContextExtensions(); } public setManifest(manifest?: ManifestSection) { @@ -25,6 +30,18 @@ export class UmbSectionContext extends UmbContextBase { getPathname() { return this.#manifestPathname.getValue(); } -} -export const UMB_SECTION_CONTEXT = new UmbContextToken('UmbSectionContext'); + #createSectionContextExtensions() { + if (this.#sectionContextExtensionController) { + this.#sectionContextExtensionController.destroy(); + } + + this.#sectionContextExtensionController = new UmbExtensionsApiInitializer( + this, + umbExtensionsRegistry, + 'sectionContext', + [], + undefined, + ); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/expansion-manager/tree-expansion-manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/expansion-manager/tree-expansion-manager.ts index a2144f8c1c..26f6bba57d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/expansion-manager/tree-expansion-manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/expansion-manager/tree-expansion-manager.ts @@ -1,7 +1,8 @@ import type { UmbTreeExpansionModel } from './types.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; -import { UmbArrayState, type Observable } from '@umbraco-cms/backoffice/observable-api'; +import type { Observable } from '@umbraco-cms/backoffice/observable-api'; +import { UmbEntityExpansionManager } from '@umbraco-cms/backoffice/utils'; /** * Manages the expansion state of a tree @@ -10,8 +11,8 @@ import { UmbArrayState, type Observable } from '@umbraco-cms/backoffice/observab * @augments {UmbControllerBase} */ export class UmbTreeExpansionManager extends UmbControllerBase { - #expansion = new UmbArrayState([], (x) => x.unique); - expansion = this.#expansion.asObservable(); + #manager = new UmbEntityExpansionManager(this); + expansion = this.#manager.expansion; /** * Checks if an entity is expanded @@ -22,9 +23,7 @@ export class UmbTreeExpansionManager extends UmbControllerBase { * @memberof UmbTreeExpansionManager */ isExpanded(entity: UmbEntityModel): Observable { - return this.#expansion.asObservablePart((entries) => - entries?.some((entry) => entry.entityType === entity.entityType && entry.unique === entity.unique), - ); + return this.#manager.isExpanded(entity); } /** @@ -34,7 +33,7 @@ export class UmbTreeExpansionManager extends UmbControllerBase { * @returns {void} */ setExpansion(expansion: UmbTreeExpansionModel): void { - this.#expansion.setValue(expansion); + this.#manager.setExpansion(expansion); } /** @@ -43,7 +42,7 @@ export class UmbTreeExpansionManager extends UmbControllerBase { * @returns {UmbTreeExpansionModel} The expansion state */ getExpansion(): UmbTreeExpansionModel { - return this.#expansion.getValue(); + return this.#manager.getExpansion(); } /** @@ -55,7 +54,7 @@ export class UmbTreeExpansionManager extends UmbControllerBase { * @returns {Promise} */ public async expandItem(entity: UmbEntityModel): Promise { - this.#expansion.appendOne(entity); + this.#manager.expandItem(entity); } /** @@ -67,7 +66,7 @@ export class UmbTreeExpansionManager extends UmbControllerBase { * @returns {Promise} */ public async collapseItem(entity: UmbEntityModel): Promise { - this.#expansion.filter((x) => x.entityType !== entity.entityType || x.unique !== entity.unique); + this.#manager.collapseItem(entity); } /** @@ -76,6 +75,6 @@ export class UmbTreeExpansionManager extends UmbControllerBase { * @returns {Promise} */ public async collapseAll(): Promise { - this.#expansion.setValue([]); + this.#manager.collapseAll(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/expansion-manager/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/expansion-manager/types.ts index 7d5c050029..0585b8ed8c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/expansion-manager/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/expansion-manager/types.ts @@ -1,3 +1,3 @@ -import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; +import type { UmbEntityExpansionModel } from '@umbraco-cms/backoffice/utils'; -export type UmbTreeExpansionModel = Array; +export type UmbTreeExpansionModel = UmbEntityExpansionModel; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/index.ts index c947f5199f..daa67a8789 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/index.ts @@ -7,7 +7,7 @@ export * from './entity-actions/reload-tree-item-children/index.js'; export * from './entity-actions/sort-children-of/index.js'; export * from './folder/index.js'; export * from './tree-item/index.js'; -export * from './tree-menu-item-default/index.js'; +export * from './tree-menu-item/index.js'; export * from './tree.element.js'; export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/manifests.ts index e1972b17cc..ec8a28e85f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/manifests.ts @@ -1,8 +1,9 @@ -import { manifests as folderManifests } from './folder/manifests.js'; import { manifests as defaultTreeItemManifests } from './tree-item/tree-item-default/manifests.js'; import { manifests as defaultTreeManifests } from './default/manifests.js'; -import { manifests as treePickerManifests } from './tree-picker-modal/manifests.js'; import { manifests as entityActionManifests } from './entity-actions/manifests.js'; +import { manifests as folderManifests } from './folder/manifests.js'; +import { manifests as treeMenuItemManifests } from './tree-menu-item/manifests.js'; +import { manifests as treePickerManifests } from './tree-picker-modal/manifests.js'; import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; export const manifests: Array = [ @@ -10,5 +11,6 @@ export const manifests: Array = ...defaultTreeManifests, ...entityActionManifests, ...folderManifests, + ...treeMenuItemManifests, ...treePickerManifests, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item-default/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item-default/index.ts deleted file mode 100644 index af047cc2d4..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item-default/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './tree-menu-item-default.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item-default/tree-menu-item-default.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item-default/tree-menu-item-default.element.ts deleted file mode 100644 index 93ce54fa6d..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item-default/tree-menu-item-default.element.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { ManifestMenuItemTreeKind } from './types.js'; -import { html, nothing, customElement, property } from '@umbraco-cms/backoffice/external/lit'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; -import type { UmbMenuItemElement } from '@umbraco-cms/backoffice/menu'; - -// TODO: Move to separate file: -const manifest: UmbExtensionManifestKind = { - type: 'kind', - alias: 'Umb.Kind.Tree', - matchKind: 'tree', - matchType: 'menuItem', - manifest: { - type: 'menuItem', - elementName: 'umb-menu-item-tree-default', - }, -}; -umbExtensionsRegistry.register(manifest); - -@customElement('umb-menu-item-tree-default') -export class UmbMenuItemTreeDefaultElement extends UmbLitElement implements UmbMenuItemElement { - @property({ type: Object }) - manifest?: ManifestMenuItemTreeKind; - - override render() { - return this.manifest - ? html` - - ` - : nothing; - } -} - -export default UmbMenuItemTreeDefaultElement; - -declare global { - interface HTMLElementTagNameMap { - 'umb-menu-item-tree-default': UmbMenuItemTreeDefaultElement; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item/index.ts new file mode 100644 index 0000000000..c4d5f8a3f0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item/index.ts @@ -0,0 +1 @@ +export * from './tree-menu-item.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item/manifests.ts new file mode 100644 index 0000000000..1ace3b635e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item/manifests.ts @@ -0,0 +1,14 @@ +import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [ + { + type: 'kind', + alias: 'Umb.Kind.Tree', + matchKind: 'tree', + matchType: 'menuItem', + manifest: { + type: 'menuItem', + element: () => import('./tree-menu-item.element.js'), + }, + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item/tree-menu-item.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item/tree-menu-item.element.ts new file mode 100644 index 0000000000..62624dd3d5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item/tree-menu-item.element.ts @@ -0,0 +1,87 @@ +import type { ManifestMenuItemTreeKind } from './types.js'; +import { html, nothing, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UMB_MENU_ITEM_CONTEXT, type UmbMenuItemElement } from '@umbraco-cms/backoffice/menu'; +import { + UmbExpansionEntryCollapsedEvent, + UmbExpansionEntryExpandedEvent, + type UmbEntityExpansionModel, +} from '@umbraco-cms/backoffice/utils'; + +@customElement('umb-menu-item-tree-default') +export class UmbMenuItemTreeDefaultElement extends UmbLitElement implements UmbMenuItemElement { + @property({ type: Object }) + manifest?: ManifestMenuItemTreeKind; + + @state() + private _menuItemExpansion: UmbEntityExpansionModel = []; + + #menuItemContext?: typeof UMB_MENU_ITEM_CONTEXT.TYPE; + #muteStateUpdate = false; + + constructor() { + super(); + + this.consumeContext(UMB_MENU_ITEM_CONTEXT, (context) => { + this.#menuItemContext = context; + this.#observeExpansion(); + }); + } + + #observeExpansion() { + this.observe(this.#menuItemContext?.expansion.expansion, (items) => { + if (this.#muteStateUpdate) return; + this._menuItemExpansion = items || []; + }); + } + + #onExpansionChange(event: UmbExpansionEntryExpandedEvent | UmbExpansionEntryCollapsedEvent) { + event.stopPropagation(); + const eventEntry = event.entry; + + if (!eventEntry) { + throw new Error('Entry is required to toggle expansion.'); + } + + if (!this.manifest) { + throw new Error('Manifest is required to toggle expansion.'); + } + + if (event.type === UmbExpansionEntryExpandedEvent.TYPE) { + this.#muteStateUpdate = true; + this.#menuItemContext?.expansion.expandItem({ ...eventEntry, menuItemAlias: this.manifest.alias }); + this.#muteStateUpdate = false; + } else if (event.type === UmbExpansionEntryCollapsedEvent.TYPE) { + this.#muteStateUpdate = true; + this.#menuItemContext?.expansion.collapseItem({ ...eventEntry, menuItemAlias: this.manifest.alias }); + this.#muteStateUpdate = false; + } + } + + override render() { + return this.manifest + ? html` + + ` + : nothing; + } +} + +export default UmbMenuItemTreeDefaultElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-menu-item-tree-default': UmbMenuItemTreeDefaultElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item-default/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item/types.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item-default/types.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-menu-item/types.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree.element.ts index 889ab0849b..a091f5e732 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree.element.ts @@ -18,6 +18,13 @@ export class UmbTreeElement extends UmbExtensionElementAndApiSlotElementBase extends UmbControllerBase { + protected _expansion = new UmbArrayState([], (x) => x.entityType + x.unique); + expansion = this._expansion.asObservable(); + + /** + * Checks if an entity is expanded + * @param {EntryModelType} entity The entity to check + * @returns {Observable} True if the entity is expanded + * @memberof UmbEntityExpansionManager + */ + isExpanded(entity: EntryModelType): Observable { + return this._expansion.asObservablePart((entries) => + entries?.some((entry) => entry.entityType === entity.entityType && entry.unique === entity.unique), + ); + } + + /** + * Sets the expansion state + * @param {UmbEntityExpansionModel | undefined} expansion The expansion state + * @memberof UmbEntityExpansionManager + * @returns {void} + */ + setExpansion(expansion: UmbEntityExpansionModel): void { + this._expansion.setValue(expansion); + this.getHostElement()?.dispatchEvent(new UmbExpansionChangeEvent()); + } + + /** + * Gets the expansion state + * @memberof UmbEntityExpansionManager + * @returns {UmbEntityExpansionModel} The expansion state + */ + getExpansion(): UmbEntityExpansionModel { + return this._expansion.getValue(); + } + + /** + * Expands an entity + * @param {EntryModelType} entity The entity to open + * @memberof UmbEntityExpansionManager + * @returns {Promise} + */ + public async expandItem(entity: EntryModelType): Promise { + this._expansion.appendOne(entity); + this.getHostElement()?.dispatchEvent(new UmbExpansionEntryExpandedEvent(entity)); + this.getHostElement()?.dispatchEvent(new UmbExpansionChangeEvent()); + } + + /** + * Expands multiple entities + * @param {UmbEntityExpansionModel} entities The entities to open + * @memberof UmbEntityExpansionManager + * @returns {void} + */ + public expandItems(entities: UmbEntityExpansionModel): void { + if (!entities || entities.length === 0) return; + this._expansion.append(entities); + entities.forEach((entity) => { + this.getHostElement()?.dispatchEvent(new UmbExpansionEntryExpandedEvent(entity)); + }); + this.getHostElement()?.dispatchEvent(new UmbExpansionChangeEvent()); + } + + /** + * Collapses an entity + * @param {EntryModelType} entry The entity to open + * @memberof UmbEntityExpansionManager + * @returns {Promise} + */ + public async collapseItem(entry: EntryModelType): Promise { + this._expansion.filter((x) => x.entityType !== entry.entityType || x.unique !== entry.unique); + this.getHostElement()?.dispatchEvent(new UmbExpansionEntryCollapsedEvent(entry)); + this.getHostElement()?.dispatchEvent(new UmbExpansionChangeEvent()); + } + + /** + * Collapses multiple entities + * @param {UmbEntityExpansionModel} entries The entities to close + * @memberof UmbEntityExpansionManager + * @returns {void} + */ + public collapseItems(entries: UmbEntityExpansionModel): void { + if (!entries || entries.length === 0) return; + this._expansion.filter( + (x) => !entries.some((entry) => entry.entityType === x.entityType && entry.unique === x.unique), + ); + entries.forEach((entry) => { + this.getHostElement()?.dispatchEvent(new UmbExpansionEntryCollapsedEvent(entry)); + }); + this.getHostElement()?.dispatchEvent(new UmbExpansionChangeEvent()); + } + + /** + * Collapses all expanded entities + * @memberof UmbEntityExpansionManager + * @returns {Promise} + */ + public async collapseAll(): Promise { + this._expansion.setValue([]); + this.getHostElement()?.dispatchEvent(new UmbExpansionChangeEvent()); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/events/expansion-change.event.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/events/expansion-change.event.ts new file mode 100644 index 0000000000..bfc0374014 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/events/expansion-change.event.ts @@ -0,0 +1,8 @@ +export class UmbExpansionChangeEvent extends Event { + public static readonly TYPE = 'expansion-change'; + + public constructor() { + // mimics the native change event + super(UmbExpansionChangeEvent.TYPE, { bubbles: true, composed: false, cancelable: false }); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/events/expansion-entry-collapsed.event.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/events/expansion-entry-collapsed.event.ts new file mode 100644 index 0000000000..b946982ab9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/events/expansion-entry-collapsed.event.ts @@ -0,0 +1,12 @@ +import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; + +export class UmbExpansionEntryCollapsedEvent extends Event { + public static readonly TYPE = 'expansion-entry-collapsed'; + entry: EntryModelType; + + public constructor(entry: EntryModelType) { + // mimics the native change event + super(UmbExpansionEntryCollapsedEvent.TYPE, { bubbles: true, composed: false, cancelable: false }); + this.entry = entry; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/events/expansion-entry-expanded.event.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/events/expansion-entry-expanded.event.ts new file mode 100644 index 0000000000..0831115d91 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/events/expansion-entry-expanded.event.ts @@ -0,0 +1,12 @@ +import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; + +export class UmbExpansionEntryExpandedEvent extends Event { + public static readonly TYPE = 'expansion-entry-expanded'; + entry: EntryModelType; + + public constructor(entry: EntryModelType) { + // mimics the native change event + super(UmbExpansionEntryExpandedEvent.TYPE, { bubbles: true, composed: false, cancelable: false }); + this.entry = entry; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/events/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/events/index.ts new file mode 100644 index 0000000000..7a4b74e034 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/events/index.ts @@ -0,0 +1,3 @@ +export * from './expansion-change.event.js'; +export * from './expansion-entry-collapsed.event.js'; +export * from './expansion-entry-expanded.event.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/index.ts new file mode 100644 index 0000000000..ee4761b07d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/index.ts @@ -0,0 +1,4 @@ +export * from './entity-expansion.manager.js'; +export * from './events/index.js'; +export * from './utils/index.js'; +export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/types.ts new file mode 100644 index 0000000000..ada5d4b7dd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/types.ts @@ -0,0 +1,9 @@ +import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; + +export type UmbEntityExpansionModel< + EntryModelType extends UmbEntityExpansionEntryModel = UmbEntityExpansionEntryModel, +> = Array; + +export interface UmbEntityExpansionEntryModel extends UmbEntityModel { + target?: UmbEntityModel; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/utils/index.ts new file mode 100644 index 0000000000..510a193d39 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/utils/index.ts @@ -0,0 +1 @@ +export * from './link-entity-expansion-entries.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/utils/link-entity-expansion-entries.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/utils/link-entity-expansion-entries.test.ts new file mode 100644 index 0000000000..5c9ec73183 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/utils/link-entity-expansion-entries.test.ts @@ -0,0 +1,26 @@ +import { expect } from '@open-wc/testing'; +import { linkEntityExpansionEntries } from './link-entity-expansion-entries'; + +describe('LinkEntityExpansionEntries', () => { + const input = [ + { entityType: 'document', unique: '1' }, + { entityType: 'document', unique: '2' }, + { entityType: 'document', unique: '3' }, + ]; + + it('should return an array of expansion entries with target', () => { + const result = linkEntityExpansionEntries(input); + expect(result).to.be.an('array').that.has.lengthOf(3); + expect(result[0]).to.have.property('entityType', 'document'); + expect(result[0]).to.have.property('unique', '1'); + expect(result[0]).to.have.property('target').that.deep.equals({ entityType: 'document', unique: '2' }); + + expect(result[1]).to.have.property('entityType', 'document'); + expect(result[1]).to.have.property('unique', '2'); + expect(result[1]).to.have.property('target').that.deep.equals({ entityType: 'document', unique: '3' }); + + expect(result[2]).to.have.property('entityType', 'document'); + expect(result[2]).to.have.property('unique', '3'); + expect(result[2]).to.not.have.property('target'); + }); +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/utils/link-entity-expansion-entries.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/utils/link-entity-expansion-entries.ts new file mode 100644 index 0000000000..04759ec3a1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/expansion/utils/link-entity-expansion-entries.ts @@ -0,0 +1,22 @@ +import type { UmbEntityExpansionEntryModel, UmbEntityExpansionModel } from '../types.js'; +import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; + +export const linkEntityExpansionEntries = (data: Array): UmbEntityExpansionModel => { + return data.map((item, index) => { + const result: UmbEntityExpansionEntryModel = { + entityType: item.entityType, + unique: item.unique, + }; + + const next = data[index + 1]; + + if (next) { + result.target = { + entityType: next.entityType, + unique: next.unique, + }; + } + + return result; + }); +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts index da5a0ec8c6..99bd098a02 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts @@ -5,6 +5,7 @@ export * from './deprecation/index.js'; export * from './diff/index.js'; export * from './direction/index.js'; export * from './download/blob-download.function.js'; +export * from './expansion/index.js'; export * from './get-guid-from-udi.function.js'; export * from './get-processed-image-url.function.js'; export * from './guard-manager/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/path/stored-path.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/path/stored-path.function.ts index e9196ab324..6f485bf991 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/path/stored-path.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/path/stored-path.function.ts @@ -30,3 +30,20 @@ export function setStoredPath(path: string): void { } sessionStorage.setItem(UMB_STORAGE_REDIRECT_URL, url.toString()); } + +/** + * Redirect the user to the stored path or the base path if not available. + * If the basePath matches the start of the stored path, the browser will replace the state instead of redirecting. + * @param {string} basePath - The base path to redirect to if no stored path is available. + * @param {boolean} force - If true, will redirect using Location + */ +export function redirectToStoredPath(basePath: string, force = false): void { + const url = retrieveStoredPath(); + const isBackofficePath = url?.pathname.startsWith(basePath) ?? false; + + if (isBackofficePath && !force) { + history.replaceState(null, '', url?.toString() ?? ''); + } else { + window.location.href = url?.toString() ?? basePath; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/index.ts index 99f8c0d35a..08f66aede1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/index.ts @@ -3,3 +3,4 @@ export * from './form-control-validator.controller.js'; export * from './observe-validation-state.controller.js'; export * from './validation-path-translation/index.js'; export * from './validation.controller.js'; +export * from './value-validator/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/value-validator/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/value-validator/index.ts new file mode 100644 index 0000000000..4e9b121518 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/value-validator/index.ts @@ -0,0 +1 @@ +export * from './value-validator.controller.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/value-validator/value-validator.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/value-validator/value-validator.controller.ts new file mode 100644 index 0000000000..dd7c1cf99c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/controllers/value-validator/value-validator.controller.ts @@ -0,0 +1,112 @@ +import { UMB_VALIDATION_CONTEXT } from '../../context/validation.context-token.js'; +import type { UmbValidator } from '../../interfaces/validator.interface.js'; +import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY } from '../../const.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; + +export interface UmbValueValidatorArgs { + dataPath?: string; + check?: (value: ValueType) => boolean; + message?: () => string; + navigateToError?: () => void; +} + +/** + * UmbValueValidator is a controller that implements the UmbValidator interface. + * It validates a value based on a provided check function, manages validation state, + * and communicates validation messages to the validation context. + * It can also handle navigation to the first invalid element if needed. + */ +export class UmbValueValidator extends UmbControllerBase implements UmbValidator { + // + #validationContext?: typeof UMB_VALIDATION_CONTEXT.TYPE; + #validationMode: boolean = false; + + #dataPath: undefined | string; + #check: (value: ValueType) => boolean; + #message: () => string; + #navigateToError?: () => void; + + #isValid: boolean = false; + #value?: ValueType; + + set value(value: ValueType | undefined) { + this.#value = value; + this.runCheck(); + } + get value(): ValueType | undefined { + return this.#value; + } + + constructor(host: UmbControllerHost, args: UmbValueValidatorArgs) { + super(host); + this.#dataPath = args.dataPath; + this.#check = + args.check ?? + ((value: ValueType) => { + return value === undefined || value === null || value === ''; + }); + this.#message = args.message ?? (() => UMB_VALIDATION_EMPTY_LOCALIZATION_KEY); + this.#navigateToError = args.navigateToError; + + this.consumeContext(UMB_VALIDATION_CONTEXT, (context) => { + if (this.#validationContext !== context) { + this.#validationContext?.removeValidator(this); + this.#validationContext = context; + this.#validationContext?.addValidator(this); + this.runCheck(); + } + }); + } + + runCheck() { + if (!this.#validationMode) return; + // Check validation: + this.#isValid = !this.#check(this.#value as ValueType); + + // Update validation message: + if (this.#validationContext) { + if (this.#dataPath) { + if (this.#isValid) { + this.#validationContext.messages.removeMessagesByTypeAndPath('custom', this.#dataPath); + } else { + this.#validationContext.messages.addMessage('custom', this.#dataPath, this.#message()); + } + } + } + } + + async validate(): Promise { + this.#validationMode = true; + // Validate is called when the validation state of this validator is asked to be fully resolved. Like when user clicks Save & Publish. + // If you need to ask the server then it could be done here, instead of asking the server each time the value changes. + // In this particular example we do not need to do anything, because our validation is represented via a message that we always set no matter the user interaction. + // If we did not like to only to check the Validation State when absolute needed then this method must be implemented. + return this.runCheck(); + } + + get isValid(): boolean { + return this.#isValid; + } + + reset(): void { + this.#isValid = false; + this.#validationMode = false; + } + + /** + * Focus the first invalid element. + */ + focusFirstInvalidElement(): void { + this.#navigateToError?.(); + } + + override destroy(): void { + if (!this.#isValid && this.#dataPath) { + this.#validationContext?.messages.removeMessagesByTypeAndPath('custom', this.#dataPath); + } + this.#validationContext = undefined; + this.#value = undefined; + super.destroy(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/view/context/view.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/view/context/view.context.ts index fdd124adca..6930c4fa94 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/view/context/view.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/view/context/view.context.ts @@ -33,6 +33,7 @@ export class UmbViewContext extends UmbControllerBase { viewAlias: viewAlias, }); this.firstHintOfVariant = mergeObservables([this.variantId, this.hints.hints], ([variantId, hints]) => { + // Notice, because we in UI have invariant fields on Variants, then we will accept invariant hints on variants. if (variantId) { return hints.find((hint) => hint.variantId ? hint.variantId.equal(variantId!) || hint.variantId.isInvariant() : true, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-breadcrumb/workspace-menu-breadcrumb/workspace-menu-breadcrumb.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-breadcrumb/workspace-menu-breadcrumb/workspace-menu-breadcrumb.element.ts index c23a374acd..0fcb8deab4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-breadcrumb/workspace-menu-breadcrumb/workspace-menu-breadcrumb.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-breadcrumb/workspace-menu-breadcrumb/workspace-menu-breadcrumb.element.ts @@ -14,6 +14,9 @@ export class UmbWorkspaceBreadcrumbElement extends UmbLitElement { @state() private _structure: UmbStructureItemModel[] = []; + @state() + private _isNew: boolean = false; + // TODO: figure out the correct context type #workspaceContext?: any; #sectionContext?: typeof UMB_SECTION_CONTEXT.TYPE; @@ -28,6 +31,7 @@ export class UmbWorkspaceBreadcrumbElement extends UmbLitElement { this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => { this.#workspaceContext = instance as any; + this.#observeIsNew(); this.#observeStructure(); this.#observeName(); }); @@ -38,16 +42,15 @@ export class UmbWorkspaceBreadcrumbElement extends UmbLitElement { }); } - #observeStructure() { - if (!this.#structureContext || !this.#workspaceContext) return; - const isNew = this.#workspaceContext.getIsNew(); - + #observeIsNew() { this.observe( - this.#structureContext.structure, + this.#workspaceContext?.isNew, (value) => { - this._structure = isNew ? value : value.slice(0, -1); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + this._isNew = value || false; }, - 'menuStructureObserver', + 'breadcrumbWorkspaceIsNewObserver', ); } @@ -61,16 +64,30 @@ export class UmbWorkspaceBreadcrumbElement extends UmbLitElement { ); } + #observeStructure() { + if (!this.#structureContext || !this.#workspaceContext) return; + + this.observe( + this.#structureContext.structure, + (value) => { + this._structure = value; + }, + 'menuStructureObserver', + ); + } + #getHref(structureItem: UmbStructureItemModel) { if (structureItem.isFolder) return undefined; return `section/${this.#sectionContext?.getPathname()}/workspace/${structureItem.entityType}/edit/${structureItem.unique}`; } override render() { + const structure = this._isNew ? this._structure : this._structure.slice(0, -1); + return html` ${map( - this._structure, + structure, (structureItem) => html`${this.localize.string(structureItem.name)} !workspaceViews.some((x) => x.manifest.alias === view.manifest.alias), ); - const diff = viewsToKeep.length !== workspaceViews.length; + const hasDiff = viewsToKeep.length !== workspaceViews.length; - if (diff) { + if (hasDiff) { const newViews = [...viewsToKeep]; // Add ones that are new: diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-editor.element.ts index 499278f5d1..72276e6ac8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-editor.element.ts @@ -136,26 +136,26 @@ export class UmbWorkspaceEditorElement extends UmbLitElement { } override render() { - return this._routes - ? html` - - ${this.#renderBackButton()} - - - ${this.#renderViews()} ${this.#renderRoutes()} - - ${when( - !this.enforceNoFooter, - () => html` - - - - - `, - )} - - ` - : nothing; + // Notice if no routes then fallback to use a slot. + // TODO: Deprecate the slot feature, to rely purely on routes, cause currently bringing an additional route would mean the slotted content would never be shown. [NL] + return html` + + ${this.#renderBackButton()} + + + ${this.#renderViews()} ${this.#renderRoutes()} + + ${when( + !this.enforceNoFooter, + () => html` + + + + + `, + )} + + `; } #renderViews() { @@ -213,7 +213,9 @@ export class UmbWorkspaceEditorElement extends UmbLitElement { } #renderRoutes() { - if (!this._routes || this._routes.length === 0) return nothing; + if (!this._routes || this._routes.length === 0 || !this._workspaceViews || this._workspaceViews.length === 0) { + return nothing; + } return html` + label=${this._variantSelectorOpen + ? this.localize.term('buttons_closeVersionSelector') + : this.localize.term('buttons_openVersionSelector')}> ${this.#getVariantSpecInfo(this._activeVariant)} ${this.#renderReadOnlyTag(this._activeVariant?.culture)} @@ -488,9 +490,9 @@ export class UmbWorkspaceSplitViewVariantSelectorElement< : html` this.#openSplitView(variant)}> - Open in Split view + ${this.localize.term('buttons_openInSplitView')} `} `; @@ -561,7 +563,9 @@ export class UmbWorkspaceSplitViewVariantSelectorElement< background: none; } - .variant:hover > .split-view { + .variant:hover > .split-view, + .variant:focus > .split-view, + .variant:focus-within > .split-view { display: flex; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/entity-detail/entity-detail-workspace-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/entity-detail/entity-detail-workspace-base.ts index 333c00a228..4b021f1abd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/entity-detail/entity-detail-workspace-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/entity-detail/entity-detail-workspace-base.ts @@ -388,11 +388,20 @@ export abstract class UmbEntityDetailWorkspaceContextBase< const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); if (!eventContext) throw new Error('Event context not found.'); - const event = new UmbRequestReloadChildrenOfEntityEvent({ + + const reloadStructureEvent = new UmbRequestReloadStructureForEntityEvent({ entityType: parent.entityType, unique: parent.unique, }); - eventContext.dispatchEvent(event); + + eventContext.dispatchEvent(reloadStructureEvent); + + const reloadChildren = new UmbRequestReloadChildrenOfEntityEvent({ + entityType: parent.entityType, + unique: parent.unique, + }); + + eventContext.dispatchEvent(reloadChildren); } protected async _update(currentData: DetailModelType) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/constants.ts index 71e09a57d2..3fa67e05c0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/constants.ts @@ -1,6 +1,7 @@ export * from './collection/constants.js'; export * from './data-type-root/constants.js'; export * from './entity-actions/constants.js'; +export * from './menu/constants.js'; export * from './modals/constants.js'; export * from './paths.js'; export * from './reference/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/menu/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/menu/constants.ts new file mode 100644 index 0000000000..c6d19226cb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/menu/constants.ts @@ -0,0 +1 @@ +export const UMB_DATA_TYPE_MENU_ITEM_ALIAS = 'Umb.MenuItem.DataTypes'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/menu/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/menu/manifests.ts index bcd1dda1ba..82e4e61b75 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/menu/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/menu/manifests.ts @@ -1,10 +1,11 @@ +import { UMB_DATA_TYPE_MENU_ITEM_ALIAS } from './constants.js'; import { UMB_WORKSPACE_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace'; export const manifests: Array = [ { type: 'menuItem', kind: 'tree', - alias: 'Umb.MenuItem.DataTypes', + alias: UMB_DATA_TYPE_MENU_ITEM_ALIAS, name: 'Data Types Menu Item', weight: 600, meta: { @@ -16,9 +17,13 @@ export const manifests: Array = [ }, { type: 'workspaceContext', + kind: 'menuStructure', name: 'Data Type Menu Structure Workspace Context', alias: 'Umb.Context.DataType.Menu.Structure', api: () => import('./data-type-menu-structure.context.js'), + meta: { + menuItemAlias: UMB_DATA_TYPE_MENU_ITEM_ALIAS, + }, conditions: [ { alias: UMB_WORKSPACE_CONDITION_ALIAS, diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/constants.ts index eb8dd5d7b1..8ed0d150bd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/constants.ts @@ -6,5 +6,6 @@ export * from './repository/constants.js'; export * from './search/constants.js'; export * from './tree/constants.js'; export * from './workspace/constants.js'; +export * from './menu-item/constants.js'; export { UMB_DICTIONARY_ROOT_ENTITY_TYPE, UMB_DICTIONARY_ENTITY_TYPE } from './entity.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/menu-item/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/menu-item/constants.ts new file mode 100644 index 0000000000..d2abd87122 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/menu-item/constants.ts @@ -0,0 +1 @@ +export const UMB_DICTIONARY_MENU_ITEM_ALIAS = 'Umb.MenuItem.Dictionary'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/menu-item/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/menu-item/manifests.ts index 03da8a1d40..109608104c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/menu-item/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/menu-item/manifests.ts @@ -1,5 +1,6 @@ import { UMB_DICTIONARY_ENTITY_TYPE } from '../entity.js'; import { UMB_DICTIONARY_TREE_ALIAS } from '../tree/index.js'; +import { UMB_DICTIONARY_MENU_ITEM_ALIAS } from './constants.js'; import { UMB_WORKSPACE_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace'; import { UMB_TRANSLATION_MENU_ALIAS } from '@umbraco-cms/backoffice/translation'; @@ -7,7 +8,7 @@ export const manifests: Array = [ { type: 'menuItem', kind: 'tree', - alias: 'Umb.MenuItem.Dictionary', + alias: UMB_DICTIONARY_MENU_ITEM_ALIAS, name: 'Dictionary Menu Item', weight: 400, meta: { @@ -21,9 +22,13 @@ export const manifests: Array = [ }, { type: 'workspaceContext', + kind: 'menuStructure', name: 'Dictionary Menu Structure Workspace Context', alias: 'Umb.Context.Dictionary.Menu.Structure', api: () => import('./dictionary-menu-structure.context.js'), + meta: { + menuItemAlias: UMB_DICTIONARY_MENU_ITEM_ALIAS, + }, conditions: [ { alias: UMB_WORKSPACE_CONDITION_ALIAS, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/constants.ts index d07e60d102..c4577efe6e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/constants.ts @@ -5,5 +5,6 @@ export * from './repository/constants.js'; export * from './search/constants.js'; export * from './tree/constants.js'; export * from './workspace/constants.js'; +export * from './menu/constants.js'; export { UMB_DOCUMENT_TYPE_ENTITY_TYPE, UMB_DOCUMENT_TYPE_ROOT_ENTITY_TYPE } from './entity.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/menu/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/menu/constants.ts new file mode 100644 index 0000000000..6f56101e30 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/menu/constants.ts @@ -0,0 +1 @@ +export const UMB_DOCUMENT_TYPE_MENU_ITEM_ALIAS = 'Umb.MenuItem.DocumentTypes'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/menu/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/menu/manifests.ts index 35ea56aca9..1e28f22619 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/menu/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/menu/manifests.ts @@ -1,11 +1,12 @@ import { UMB_DOCUMENT_TYPE_TREE_ALIAS } from '../tree/index.js'; +import { UMB_DOCUMENT_TYPE_MENU_ITEM_ALIAS } from './constants.js'; import { UMB_WORKSPACE_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace'; export const manifests: Array = [ { type: 'menuItem', kind: 'tree', - alias: 'Umb.MenuItem.DocumentTypes', + alias: UMB_DOCUMENT_TYPE_MENU_ITEM_ALIAS, name: 'Document Types Menu Item', weight: 900, meta: { @@ -16,9 +17,13 @@ export const manifests: Array = [ }, { type: 'workspaceContext', + kind: 'menuStructure', name: 'Document Type Menu Structure Workspace Context', alias: 'Umb.Context.DocumentType.Menu.Structure', api: () => import('./document-type-menu-structure.context.js'), + meta: { + menuItemAlias: UMB_DOCUMENT_TYPE_MENU_ITEM_ALIAS, + }, conditions: [ { alias: UMB_WORKSPACE_CONDITION_ALIAS, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/constants.ts index b0cd03a572..8b2ea67a99 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/constants.ts @@ -4,6 +4,7 @@ export * from './collection/constants.js'; export * from './entity-actions/constants.js'; export * from './entity-bulk-actions/constants.js'; export * from './item/constants.js'; +export * from './menu/constants.js'; export * from './modals/constants.js'; export * from './paths.js'; export * from './property-dataset-context/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/menu/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/menu/constants.ts new file mode 100644 index 0000000000..e2fd4065cc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/menu/constants.ts @@ -0,0 +1 @@ +export const UMB_DOCUMENT_MENU_ITEM_ALIAS = 'Umb.MenuItem.Document'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/menu/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/menu/manifests.ts index dc76417093..f50b532911 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/menu/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/menu/manifests.ts @@ -1,4 +1,5 @@ import { UMB_DOCUMENT_TREE_ALIAS } from '../tree/index.js'; +import { UMB_DOCUMENT_MENU_ITEM_ALIAS } from './constants.js'; import { UMB_WORKSPACE_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace'; export const UMB_CONTENT_MENU_ALIAS = 'Umb.Menu.Content'; @@ -12,7 +13,7 @@ export const manifests: Array = [ { type: 'menuItem', kind: 'tree', - alias: 'Umb.MenuItem.Document', + alias: UMB_DOCUMENT_MENU_ITEM_ALIAS, name: 'Document Menu Item', weight: 200, meta: { @@ -24,9 +25,13 @@ export const manifests: Array = [ }, { type: 'workspaceContext', + kind: 'menuStructure', name: 'Document Menu Structure Workspace Context', alias: 'Umb.Context.Document.Menu.Structure', api: () => import('./document-menu-structure.context.js'), + meta: { + menuItemAlias: UMB_DOCUMENT_MENU_ITEM_ALIAS, + }, conditions: [ { alias: UMB_WORKSPACE_CONDITION_ALIAS, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/validation/document-validation.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/validation/document-validation.server.data-source.ts index 661687bc54..68b04408a0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/validation/document-validation.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/validation/document-validation.server.data-source.ts @@ -87,10 +87,12 @@ export class UmbDocumentValidationServerDataSource { // Maybe use: tryExecuteAndNotify const { data, error } = await tryExecute( this.#host, - DocumentService.putUmbracoManagementApiV11DocumentByIdValidate11({ - path: { id: model.unique }, - body, - }), + DocumentService.putUmbracoManagementApiV1Service.documentByIdValidate1Service.putUmbracoManagementApiV11DocumentByIdValidate11( + { + path: { id: model.unique }, + body, + }, + ), { disableNotifications: true, }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/components/imaging-thumbnail.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/components/imaging-thumbnail.element.ts index d4c69d01a6..b23d79e80a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/components/imaging-thumbnail.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/components/imaging-thumbnail.element.ts @@ -1,8 +1,10 @@ import { UmbImagingCropMode } from '../types.js'; import { UmbImagingRepository } from '../imaging.repository.js'; -import { css, customElement, html, nothing, property, state, when } from '@umbraco-cms/backoffice/external/lit'; +import { css, customElement, html, property, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; +import { UmbEntityUpdatedEvent } from '@umbraco-cms/backoffice/entity-action'; @customElement('umb-imaging-thumbnail') export class UmbImagingThumbnailElement extends UmbLitElement { @@ -66,7 +68,11 @@ export class UmbImagingThumbnailElement extends UmbLitElement { #intersectionObserver?: IntersectionObserver; override render() { - return html` ${this.#renderThumbnail()} ${when(this._isLoading, () => this.#renderLoading())} `; + return when( + this._isLoading, + () => this.#renderLoading(), + () => this.#renderThumbnail(), + ); } override connectedCallback() { @@ -77,11 +83,13 @@ export class UmbImagingThumbnailElement extends UmbLitElement { if (entries[0].isIntersecting) { this.#generateThumbnailUrl(); this.#intersectionObserver?.disconnect(); + this.#observeActionEvent(); } }); this.#intersectionObserver.observe(this); } else { this.#generateThumbnailUrl(); + this.#observeActionEvent(); } } @@ -90,13 +98,26 @@ export class UmbImagingThumbnailElement extends UmbLitElement { this.#intersectionObserver?.disconnect(); } + /** + * Observes the action events for the current media item. + * Reloads the thumbnail when the media item is updated. + * Note: Call this only when the media item is in view or otherwise is supposed to be loaded already. + */ + #observeActionEvent() { + this.consumeContext(UMB_ACTION_EVENT_CONTEXT, (actionEventContext) => { + actionEventContext?.addEventListener(UmbEntityUpdatedEvent.TYPE, (event) => { + if (event instanceof UmbEntityUpdatedEvent && event.getUnique() === this.unique) { + this.#generateThumbnailUrl(); + } + }); + }); + } + #renderLoading() { return html`
`; } #renderThumbnail() { - if (this._isLoading) return nothing; - return when( this._thumbnailUrl, (url) => html`${this.alt}`, @@ -105,12 +126,14 @@ export class UmbImagingThumbnailElement extends UmbLitElement { } async #generateThumbnailUrl() { - const { data } = await this.#imagingRepository.requestThumbnailUrls( - [this.unique], - this.height, - this.width, - this.mode, - ); + if (!this.unique) return; + this._isLoading = true; + + const { data } = await this.#imagingRepository.requestResizedItems([this.unique], { + height: this.height, + width: this.width, + mode: this.mode, + }); this._thumbnailUrl = data?.[0]?.url ?? ''; this._isLoading = false; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.repository.ts index 38c9cb3697..e9b7b515c0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.repository.ts @@ -6,26 +6,30 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import type { UmbMediaUrlModel } from '@umbraco-cms/backoffice/media'; +/** + * Repository for managing imaging-related data. + * You can use it to request (cached) thumbnail URLs or new resized images. + */ export class UmbImagingRepository extends UmbRepositoryBase implements UmbApi { + #init; + #itemSource = new UmbImagingServerDataSource(this); #dataStore?: typeof UMB_IMAGING_STORE_CONTEXT.TYPE; - #itemSource: UmbImagingServerDataSource; constructor(host: UmbControllerHost) { super(host); - this.#itemSource = new UmbImagingServerDataSource(host); - this.consumeContext(UMB_IMAGING_STORE_CONTEXT, (instance) => { + this.#init = this.consumeContext(UMB_IMAGING_STORE_CONTEXT, (instance) => { if (instance) { this.#dataStore = instance; } - }); + }).asPromise({ preventTimeout: true }); } /** * Requests the items for the given uniques * @param {Array} uniques - The uniques * @param {UmbImagingResizeModel} imagingModel - The imaging model - * @returns {Promise<{ data: UmbMediaUrlModel[] }>} + * @returns {Promise<{ data: UmbMediaUrlModel[] }>} - The resized absolute media URLs * @memberof UmbImagingRepository */ async requestResizedItems( @@ -33,12 +37,17 @@ export class UmbImagingRepository extends UmbRepositoryBase implements UmbApi { imagingModel?: UmbImagingResizeModel, ): Promise<{ data: UmbMediaUrlModel[] }> { if (!uniques.length) throw new Error('Uniques are missing'); - if (!this.#dataStore) throw new Error('Data store is missing'); + + await this.#init; + + if (!this.#dataStore) { + console.warn('[UmbImagingRepository] No data store available. All thumbnails are uncached.'); + } const urls = new Map(); for (const unique of uniques) { - const existingCrop = this.#dataStore.getCrop(unique, imagingModel); + const existingCrop = this.#dataStore?.getCrop(unique, imagingModel); if (existingCrop !== undefined) { urls.set(unique, existingCrop); continue; @@ -53,7 +62,7 @@ export class UmbImagingRepository extends UmbRepositoryBase implements UmbApi { const url = urlModels?.[0]?.url; - this.#dataStore.addCrop(unique, url ?? '', imagingModel); + this.#dataStore?.addCrop(unique, url ?? '', imagingModel); if (url) { urls.set(unique, url); @@ -64,14 +73,32 @@ export class UmbImagingRepository extends UmbRepositoryBase implements UmbApi { } /** - * Requests the thumbnail URLs for the given uniques - * @param {Array} uniques - * @param {number} height - * @param {number} width - * @param {ImageCropModeModel} mode - The crop mode - * @memberof UmbImagingRepository + * Internal method to clear the cache for a specific image. + * @param {string} unique The unique identifier for the media item + * @internal */ - async requestThumbnailUrls(uniques: Array, height: number, width: number, mode = UmbImagingCropMode.MIN) { + // eslint-disable-next-line @typescript-eslint/naming-convention + async _internal_clearCropByUnique(unique: string) { + await this.#init; + this.#dataStore?.clearCropByUnique(unique); + } + + /** + * Requests the thumbnail URLs for the given uniques + * @param {Array} uniques - The unique identifiers for the media items + * @param {number} height - The desired height in pixels of the thumbnail + * @param {number} width - The desired width in pixels of the thumbnail + * @param {UmbImagingCropMode} mode - The crop mode + * @returns {Promise<{ data: UmbMediaUrlModel[] }>} - The resized absolute media URLs + * @memberof UmbImagingRepository + * @deprecated Use {@link UmbImagingRepository#requestResizedItems} instead for more flexibility and control over the imaging model. This will be removed in Umbraco 18. + */ + async requestThumbnailUrls( + uniques: Array, + height: number, + width: number, + mode: UmbImagingCropMode = UmbImagingCropMode.MIN, + ): Promise<{ data: UmbMediaUrlModel[] }> { const imagingModel: UmbImagingResizeModel = { height, width, mode }; return this.requestResizedItems(uniques, imagingModel); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.store.ts b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.store.ts index 8b4bf068bd..29e832a9e6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.store.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/imaging/imaging.store.ts @@ -5,11 +5,10 @@ import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; export class UmbImagingStore extends UmbContextBase implements UmbApi { - #data; + #data = new Map>(); constructor(host: UmbControllerHost) { super(host, UMB_IMAGING_STORE_CONTEXT.toString()); - this.#data = new Map>(); } /** @@ -17,7 +16,7 @@ export class UmbImagingStore extends UmbContextBase implements UmbApi { * @param {string} unique - The media key * @returns {Map | undefined} - The data if it exists */ - getData(unique: string) { + getData(unique: string): Map | undefined { return this.#data.get(unique); } @@ -27,7 +26,7 @@ export class UmbImagingStore extends UmbContextBase implements UmbApi { * @param {string} data - The resize configuration * @returns {string | undefined} - The crop if it exists */ - getCrop(unique: string, data?: UmbImagingResizeModel) { + getCrop(unique: string, data?: UmbImagingResizeModel): string | undefined { return this.#data.get(unique)?.get(this.#generateCropKey(data)); } @@ -35,7 +34,7 @@ export class UmbImagingStore extends UmbContextBase implements UmbApi { * Adds a new crop to the store. * @param {string} unique - The media key * @param {string} urlInfo - The URL of the crop - * @param { | undefined} data - The resize configuration + * @param {UmbImagingResizeModel | undefined} data - The resize configuration */ addCrop(unique: string, urlInfo: string, data?: UmbImagingResizeModel) { if (!this.#data.has(unique)) { @@ -44,12 +43,36 @@ export class UmbImagingStore extends UmbContextBase implements UmbApi { this.#data.get(unique)?.set(this.#generateCropKey(data), urlInfo); } + /** + * Clears all crops from the store. + */ + clear() { + this.#data.clear(); + } + + /** + * Clears the crop for a specific unique identifier. + * @param {string} unique - The unique identifier for the media item + */ + clearCropByUnique(unique: string) { + this.#data.delete(unique); + } + + /** + * Clears the crop for a specific unique identifier and resize configuration. + * @param {string} unique - The unique identifier for the media item + * @param {UmbImagingResizeModel | undefined} data - The resize configuration + */ + clearCropByConfiguration(unique: string, data?: UmbImagingResizeModel) { + this.#data.get(unique)?.delete(this.#generateCropKey(data)); + } + /** * Generates a unique key for the crop based on the width, height and mode. * @param {UmbImagingResizeModel} data - The resize configuration * @returns {string} - The crop key */ - #generateCropKey(data?: UmbImagingResizeModel) { + #generateCropKey(data?: UmbImagingResizeModel): string { return data ? `${data.width}x${data.height};${data.mode}` : 'generic'; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/constants.ts index c80e3ac5e4..86c491dd00 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/constants.ts @@ -1,11 +1,12 @@ export * from './entity-actions/constants.js'; export * from './media-type-root/constants.js'; +export * from './menu/constants.js'; export * from './paths.js'; export * from './property-type/constants.js'; export * from './repository/constants.js'; +export * from './search/constants.js'; export * from './tree/constants.js'; export * from './workspace/constants.js'; -export * from './search/constants.js'; export { UMB_MEDIA_TYPE_ENTITY_TYPE, diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/menu/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/menu/constants.ts new file mode 100644 index 0000000000..462c1600e7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/menu/constants.ts @@ -0,0 +1 @@ +export const UMB_MEDIA_TYPE_MENU_ITEM_ALIAS = 'Umb.MenuItem.MediaTypes'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/menu/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/menu/manifests.ts index 65107a2596..b4ea0ec9b8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/menu/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/menu/manifests.ts @@ -1,11 +1,12 @@ import { UMB_MEDIA_TYPE_TREE_ALIAS } from '../constants.js'; +import { UMB_MEDIA_TYPE_MENU_ITEM_ALIAS } from './constants.js'; import { UMB_WORKSPACE_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace'; export const manifests: Array = [ { type: 'menuItem', kind: 'tree', - alias: 'Umb.MenuItem.MediaTypes', + alias: UMB_MEDIA_TYPE_MENU_ITEM_ALIAS, name: 'Media Types Menu Item', weight: 800, meta: { @@ -16,9 +17,13 @@ export const manifests: Array = [ }, { type: 'workspaceContext', + kind: 'menuStructure', name: 'Media Type Menu Structure Workspace Context', alias: 'Umb.Context.MediaType.Menu.Structure', api: () => import('./media-type-menu-structure.context.js'), + meta: { + menuItemAlias: UMB_MEDIA_TYPE_MENU_ITEM_ALIAS, + }, conditions: [ { alias: UMB_WORKSPACE_CONDITION_ALIAS, diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/grid/media-grid-collection-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/grid/media-grid-collection-view.element.ts index 141443fd19..37492a1f51 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/grid/media-grid-collection-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/views/grid/media-grid-collection-view.element.ts @@ -7,9 +7,9 @@ import { css, customElement, html, ifDefined, repeat, state } from '@umbraco-cms import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbFileDropzoneItemStatus } from '@umbraco-cms/backoffice/dropzone'; +import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/router'; import '@umbraco-cms/backoffice/imaging'; -import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/router'; @customElement('umb-media-grid-collection-view') export class UmbMediaGridCollectionViewElement extends UmbLitElement { @@ -104,7 +104,7 @@ export class UmbMediaGridCollectionViewElement extends UmbLitElement { @selected=${() => this.#onSelect(item)} @deselected=${() => this.#onDeselect(item)}> diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/menu/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/menu/constants.ts index 11ceb55bf0..34b70788f4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/menu/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/menu/constants.ts @@ -1 +1,2 @@ export const UMB_MEDIA_MENU_ALIAS = 'Umb.Menu.Media'; +export const UMB_MEDIA_MENU_ITEM_ALIAS = 'Umb.MenuItem.Media'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/menu/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/menu/manifests.ts index a4bd3d6b8a..93bb4b6b5b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/menu/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/menu/manifests.ts @@ -1,5 +1,5 @@ import { UMB_MEDIA_TREE_ALIAS } from '../constants.js'; -import { UMB_MEDIA_MENU_ALIAS } from './constants.js'; +import { UMB_MEDIA_MENU_ALIAS, UMB_MEDIA_MENU_ITEM_ALIAS } from './constants.js'; import { UMB_WORKSPACE_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace'; export const manifests: Array = [ @@ -11,7 +11,7 @@ export const manifests: Array = [ { type: 'menuItem', kind: 'tree', - alias: 'Umb.MenuItem.Media', + alias: UMB_MEDIA_MENU_ITEM_ALIAS, name: 'Media Menu Item', weight: 100, meta: { @@ -23,9 +23,13 @@ export const manifests: Array = [ }, { type: 'workspaceContext', + kind: 'menuStructure', name: 'Media Menu Structure Workspace Context', alias: 'Umb.Context.Media.Menu.Structure', api: () => import('./media-menu-structure.context.js'), + meta: { + menuItemAlias: UMB_MEDIA_MENU_ITEM_ALIAS, + }, conditions: [ { alias: UMB_WORKSPACE_CONDITION_ALIAS, diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/repository/detail/media-detail.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/repository/detail/media-detail.repository.ts index 3476a5b71a..aa3e6b4892 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/repository/detail/media-detail.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/repository/detail/media-detail.repository.ts @@ -1,13 +1,43 @@ import type { UmbMediaDetailModel } from '../../types.js'; import { UmbMediaServerDataSource } from './media-detail.server.data-source.js'; import { UMB_MEDIA_DETAIL_STORE_CONTEXT } from './media-detail.store.context-token.js'; +import { UmbImagingRepository } from '@umbraco-cms/backoffice/imaging'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbDetailRepositoryBase } from '@umbraco-cms/backoffice/repository'; export class UmbMediaDetailRepository extends UmbDetailRepositoryBase { + #imagingRepository = new UmbImagingRepository(this); constructor(host: UmbControllerHost) { super(host, UmbMediaServerDataSource, UMB_MEDIA_DETAIL_STORE_CONTEXT); } + + /** + * @inheritdoc + */ + override async save(model: UmbMediaDetailModel) { + const result = await super.save(model); + + if (!result.error) { + // Attempt to clear image cache + await this.#imagingRepository._internal_clearCropByUnique(model.unique); + } + + return result; + } + + /** + * @inheritdoc + */ + override async delete(unique: string) { + const result = await super.delete(unique); + + if (!result.error) { + // Attempt to clear image cache + await this.#imagingRepository._internal_clearCropByUnique(unique); + } + + return result; + } } export { UmbMediaDetailRepository as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/constants.ts index 5f144502c4..1c409bd6f9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/constants.ts @@ -1,5 +1,6 @@ export * from './entity-actions/constants.js'; export * from './member-type-root/constants.js'; +export * from './menu/constants.js'; export * from './paths.js'; export * from './property-type/constants.js'; export * from './repository/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/menu/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/menu/constants.ts new file mode 100644 index 0000000000..9f23bcf60c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/menu/constants.ts @@ -0,0 +1 @@ +export const UMB_MEMBER_TYPE_MENU_ITEM_ALIAS = 'Umb.MenuItem.MemberTypes'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/menu/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/menu/manifests.ts index 9c563c1bd2..b00f960418 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/menu/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/menu/manifests.ts @@ -1,11 +1,11 @@ -import { UMB_MEMBER_TYPE_TREE_ALIAS } from '../constants.js'; +import { UMB_MEMBER_TYPE_MENU_ITEM_ALIAS, UMB_MEMBER_TYPE_TREE_ALIAS } from '../constants.js'; import { UMB_WORKSPACE_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace'; export const manifests: Array = [ { type: 'menuItem', kind: 'tree', - alias: 'Umb.MenuItem.MemberTypes', + alias: UMB_MEMBER_TYPE_MENU_ITEM_ALIAS, name: 'Member Type Menu Item', weight: 700, meta: { @@ -16,9 +16,13 @@ export const manifests: Array = [ }, { type: 'workspaceContext', + kind: 'menuStructure', name: 'Member Type Menu Structure Workspace Context', alias: 'Umb.Context.MemberType.Menu.Structure', api: () => import('./member-type-menu-structure.context.js'), + meta: { + menuItemAlias: UMB_MEMBER_TYPE_MENU_ITEM_ALIAS, + }, conditions: [ { alias: UMB_WORKSPACE_CONDITION_ALIAS, diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/validation/member-validation.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/validation/member-validation.server.data-source.ts index 07f5730c02..544069add2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/validation/member-validation.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/validation/member-validation.server.data-source.ts @@ -1,8 +1,8 @@ import type { UmbMemberDetailModel } from '../../types.js'; import { - type CreateMemberRequestModel, MemberService, type UpdateMemberRequestModel, + type PostMemberValidateData, } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { tryExecute } from '@umbraco-cms/backoffice/resources'; @@ -32,14 +32,13 @@ export class UmbMemberValidationServerDataSource { ): Promise> { if (!model) throw new Error('Member is missing'); if (!model.unique) throw new Error('Member unique is missing'); - if (!model.newPassword) throw new Error('Member newPassword is missing'); + //if (!model.newPassword) throw new Error('Member newPassword is missing'); if (parentUnique === undefined) throw new Error('Parent unique is missing'); - // TODO: make data mapper to prevent errors - const body: CreateMemberRequestModel = { + const body: PostMemberValidateData['body'] = { email: model.email, username: model.username, - password: model.newPassword, + password: model.newPassword ?? '', isApproved: model.isApproved, id: model.unique, memberType: { id: model.memberType.unique }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/constants.ts index 697ec98e91..465892c416 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/constants.ts @@ -9,4 +9,7 @@ export const UMB_MEMBER_DETAIL_MODEL_VARIANT_SCAFFOLD: UmbMemberVariantModel = { } as const; export { UMB_MEMBER_WORKSPACE_CONTEXT } from './member-workspace.context-token.js'; -export { UMB_MEMBER_WORKSPACE_ALIAS } from './manifests.js'; + +export const UMB_MEMBER_WORKSPACE_ALIAS = 'Umb.Workspace.Member'; +export const UMB_MEMBER_WORKSPACE_VIEW_CONTENT_ALIAS = 'Umb.WorkspaceView.Member.Content'; +export const UMB_MEMBER_WORKSPACE_VIEW_MEMBER_ALIAS = 'Umb.WorkspaceView.Member.Member'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/manifests.ts index aac2a61278..a16019ff71 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/manifests.ts @@ -1,4 +1,9 @@ import { UMB_MEMBER_ENTITY_TYPE } from '../../entity.js'; +import { + UMB_MEMBER_WORKSPACE_ALIAS, + UMB_MEMBER_WORKSPACE_VIEW_CONTENT_ALIAS, + UMB_MEMBER_WORKSPACE_VIEW_MEMBER_ALIAS, +} from './constants.js'; import type { ManifestWorkspaces, ManifestWorkspaceActions, @@ -7,8 +12,6 @@ import type { import { UmbSubmitWorkspaceAction, UMB_WORKSPACE_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace'; import { UMB_CONTENT_HAS_PROPERTIES_WORKSPACE_CONDITION } from '@umbraco-cms/backoffice/content'; -export const UMB_MEMBER_WORKSPACE_ALIAS = 'Umb.Workspace.Member'; - const workspace: ManifestWorkspaces = { type: 'workspace', kind: 'routable', @@ -45,7 +48,7 @@ export const workspaceViews: Array = [ { type: 'workspaceView', kind: 'contentEditor', - alias: 'Umb.WorkspaceView.Member.Content', + alias: UMB_MEMBER_WORKSPACE_VIEW_CONTENT_ALIAS, name: 'Member Workspace Content View', weight: 1000, meta: { @@ -65,7 +68,7 @@ export const workspaceViews: Array = [ }, { type: 'workspaceView', - alias: 'Umb.WorkspaceView.Member.Member', + alias: UMB_MEMBER_WORKSPACE_VIEW_MEMBER_ALIAS, name: 'Member Workspace Member View', js: () => import('./views/member/member-workspace-view-member.element.js'), weight: 500, diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/member-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/member-workspace.context.ts index ee4233573a..1ea4c9e97d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/member-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/member-workspace.context.ts @@ -1,11 +1,14 @@ -import type { UmbMemberDetailRepository } from '../../repository/index.js'; +import { UmbMemberValidationRepository, type UmbMemberDetailRepository } from '../../repository/index.js'; import type { UmbMemberDetailModel, UmbMemberVariantModel } from '../../types.js'; import { UmbMemberPropertyDatasetContext } from '../../property-dataset-context/member-property-dataset.context.js'; import { UMB_MEMBER_ENTITY_TYPE, UMB_MEMBER_ROOT_ENTITY_TYPE } from '../../entity.js'; import { UMB_MEMBER_DETAIL_REPOSITORY_ALIAS } from '../../repository/detail/manifests.js'; -import { UMB_MEMBER_WORKSPACE_ALIAS } from './manifests.js'; +import { + UMB_MEMBER_WORKSPACE_ALIAS, + UMB_MEMBER_WORKSPACE_VIEW_MEMBER_ALIAS, + UMB_MEMBER_DETAIL_MODEL_VARIANT_SCAFFOLD, +} from './constants.js'; import { UmbMemberWorkspaceEditorElement } from './member-workspace-editor.element.js'; -import { UMB_MEMBER_DETAIL_MODEL_VARIANT_SCAFFOLD } from './constants.js'; import { UmbMemberTypeDetailRepository, type UmbMemberTypeDetailModel } from '@umbraco-cms/backoffice/member-type'; import { UmbWorkspaceIsNewRedirectController, @@ -14,6 +17,9 @@ import { import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import { UmbContentDetailWorkspaceContextBase, type UmbContentWorkspaceContext } from '@umbraco-cms/backoffice/content'; +import { ensurePathEndsWithSlash } from '@umbraco-cms/backoffice/utils'; +import { UmbValueValidator } from '@umbraco-cms/backoffice/validation'; +import type { UmbValueValidatorArgs } from '@umbraco-cms/backoffice/validation'; type ContentModel = UmbMemberDetailModel; type ContentTypeModel = UmbMemberTypeDetailModel; @@ -39,8 +45,7 @@ export class UmbMemberWorkspaceContext workspaceAlias: UMB_MEMBER_WORKSPACE_ALIAS, detailRepositoryAlias: UMB_MEMBER_DETAIL_REPOSITORY_ALIAS, contentTypeDetailRepository: UmbMemberTypeDetailRepository, - // TODO: Enable Validation Repository when we have UI for showing validation issues on other tabs. [NL] - //contentValidationRepository: UmbMemberValidationRepository, + contentValidationRepository: UmbMemberValidationRepository, contentVariantScaffold: UMB_MEMBER_DETAIL_MODEL_VARIANT_SCAFFOLD, contentTypePropertyName: 'memberType', }); @@ -83,6 +88,84 @@ export class UmbMemberWorkspaceContext }, }, ]); + + this.#setupValidationCheckForRootProperty('username'); + this.#setupValidationCheckForRootProperty('email'); + const passwordValidator = this.#setupValidationCheckForRootProperty( + 'newPassword', + (value: string | null | undefined) => { + // Only when in create mode. + return this.getIsNew() === true && (value === undefined || value === null || value === ''); + }, + ); + + this.observe( + this.isNew, + () => { + passwordValidator.runCheck(); + }, + null, + ); + } + + #hintedMsgs: Set = new Set(); + + // A simple observation function to check for validation issues of a root property and map the validation message to a hint. + #setupValidationCheckForRootProperty( + propertyName: string, + checkMethod?: UmbValueValidatorArgs['check'], + ) { + const dataPath = `$.${propertyName}`; + const validator = new UmbValueValidator(this, { + dataPath, + navigateToError: () => { + const router = this.getHostElement().shadowRoot!.querySelector('umb-router-slot')!; + const routerPath = router.absoluteActiveViewPath; + if (routerPath) { + const newPath: string = ensurePathEndsWithSlash(routerPath) + 'invariant/view/info'; + // Check that we are still part of the DOM and thereby relevant: + window.history.replaceState(null, '', newPath); + } + }, + check: checkMethod, + }); + this.observe( + this._data.createObservablePartOfCurrent((data) => { + return data?.[propertyName as unknown as keyof ContentModel] as ValueType; + }), + (value) => { + validator.value = value; + }, + `validateObserverFor_${propertyName}`, + ); + + // Observe Validation and turn it into hint: + this.observe( + this.validationContext.messages.messagesOfPathAndDescendant(dataPath), + (messages) => { + messages.forEach((message) => { + if (this.#hintedMsgs.has(message.key)) return; + + this.hints.addOne({ + unique: message.key, + path: [UMB_MEMBER_WORKSPACE_VIEW_MEMBER_ALIAS], + text: '!', + color: 'invalid', + weight: 1000, + }); + this.#hintedMsgs.add(message.key); + }); + this.#hintedMsgs.forEach((key) => { + if (!messages.some((msg) => msg.key === key)) { + this.#hintedMsgs.delete(key); + this.hints.removeOne(key); + } + }); + }, + `messageObserverFor_${propertyName}`, + ); + + return validator; } async create(memberTypeUnique: string) { @@ -145,6 +228,10 @@ export class UmbMemberWorkspaceContext return this.#get('username') || ''; } + get newPassword(): string { + return this.#get('newPassword') || ''; + } + get isLockedOut(): boolean { return this.#get('isLockedOut') || false; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/views/member/member-workspace-view-member-info.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/views/member/member-workspace-view-member-info.element.ts index 834e71cc11..8998552fd1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/views/member/member-workspace-view-member-info.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/views/member/member-workspace-view-member-info.element.ts @@ -15,7 +15,7 @@ import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-reg @customElement('umb-member-workspace-view-member-info') export class UmbMemberWorkspaceViewMemberInfoElement extends UmbLitElement implements UmbWorkspaceViewElement { @state() - private _memberTypeUnique = ''; + private _memberTypeUnique?: string; @state() private _memberTypeName = ''; @@ -58,16 +58,21 @@ export class UmbMemberWorkspaceViewMemberInfoElement extends UmbLitElement imple this.consumeContext(UMB_MEMBER_WORKSPACE_CONTEXT, async (context) => { this.#workspaceContext = context; - this.observe(this.#workspaceContext?.contentTypeUnique, (unique) => (this._memberTypeUnique = unique || '')); + this.observe(this.#workspaceContext?.contentTypeUnique, async (unique) => { + if (unique === this._memberTypeUnique) return; + this._memberTypeUnique = unique; + + if (this._memberTypeUnique) { + const memberType = (await this.#memberTypeItemRepository.requestItems([this._memberTypeUnique])).data?.[0]; + if (!memberType) return; + this._memberTypeName = memberType.name; + this._memberTypeIcon = memberType.icon; + } + }); this.observe(this.#workspaceContext?.createDate, (date) => (this._createDate = this.#setDateFormat(date))); this.observe(this.#workspaceContext?.updateDate, (date) => (this._updateDate = this.#setDateFormat(date))); this.observe(this.#workspaceContext?.unique, (unique) => (this._unique = unique || '')); this.observe(this.#workspaceContext?.kind, (kind) => (this._memberKind = kind)); - - const memberType = (await this.#memberTypeItemRepository.requestItems([this._memberTypeUnique])).data?.[0]; - if (!memberType) return; - this._memberTypeName = memberType.name; - this._memberTypeIcon = memberType.icon; }); createExtensionApiByAlias(this, UMB_SECTION_USER_PERMISSION_CONDITION_ALIAS, [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/views/member/member-workspace-view-member.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/views/member/member-workspace-view-member.element.ts index 4d163bc58c..ab101f6c0e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/views/member/member-workspace-view-member.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member/views/member/member-workspace-view-member.element.ts @@ -10,6 +10,7 @@ import type { UUIBooleanInputEvent } from '@umbraco-cms/backoffice/external/uui' import './member-workspace-view-member-info.element.js'; import type { UmbInputMemberGroupElement } from '@umbraco-cms/backoffice/member-group'; import { UMB_CURRENT_USER_CONTEXT } from '@umbraco-cms/backoffice/current-user'; +import { umbBindToValidation } from '@umbraco-cms/backoffice/validation'; @customElement('umb-member-workspace-view-member') export class UmbMemberWorkspaceViewMemberElement extends UmbLitElement implements UmbWorkspaceViewElement { @@ -80,7 +81,7 @@ export class UmbMemberWorkspaceViewMemberElement extends UmbLitElement implement }; #renderPasswordInput() { - if (this._isNew) { + if (this._isNew && this._workspaceContext) { return html` this.#onPasswordUpdate()}> + @input=${() => this.#onPasswordUpdate()} + value=${this._workspaceContext.newPassword} + required + ${umbBindToValidation(this, '$.password', this._workspaceContext.newPassword)}> @@ -97,6 +101,7 @@ export class UmbMemberWorkspaceViewMemberElement extends UmbLitElement implement name="confirmPassword" label="Confirm password" type="password" + value=${this._workspaceContext.newPassword} @input=${() => this.#onPasswordUpdate()}> @@ -157,6 +162,7 @@ export class UmbMemberWorkspaceViewMemberElement extends UmbLitElement implement name="login" label=${this.localize.term('general_username')} value=${this._workspaceContext.username} + ${umbBindToValidation(this, '$.username', this._workspaceContext.username)} required required-message=${this.localize.term('user_loginnameRequired')} @input=${(e: Event) => this.#onChange('username', (e.target as HTMLInputElement).value)}> @@ -168,6 +174,7 @@ export class UmbMemberWorkspaceViewMemberElement extends UmbLitElement implement name="email" label=${this.localize.term('general_email')} value=${this._workspaceContext.email} + ${umbBindToValidation(this, '$.email', this._workspaceContext.email)} required required-message=${this.localize.term('user_emailRequired')} @input=${(e: Event) => this.#onChange('email', (e.target as HTMLInputElement).value)}> diff --git a/src/Umbraco.Web.UI.Client/src/packages/packages/package/repository/sources/package.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/packages/package/repository/sources/package.server.data.ts index 945c7fe590..094b24326e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/packages/package/repository/sources/package.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/packages/package/repository/sources/package.server.data.ts @@ -2,7 +2,7 @@ import { tryExecute } from '@umbraco-cms/backoffice/resources'; import { ManifestService, PackageService } from '@umbraco-cms/backoffice/external/backend-api'; import type { CreatePackageRequestModel, - UpdatePackageRequestModelWritable, + UpdatePackageRequestModel, } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; @@ -57,7 +57,7 @@ export class UmbPackageServerDataSource { return await tryExecute(this._host, PackageService.postPackageCreated({ body })); } - async updateCreatedPackage(id: string, body: UpdatePackageRequestModelWritable) { + async updateCreatedPackage(id: string, body: UpdatePackageRequestModel) { return await tryExecute(this._host, PackageService.putPackageCreatedById({ path: { id }, body })); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts index ea01c96fd8..ef841b82fc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/components/rte-base.element.ts @@ -21,10 +21,21 @@ import type { UmbPropertyEditorConfigCollection, } from '@umbraco-cms/backoffice/property-editor'; +/** + * The abstract base class that is used as a base for the rich-text-editor component. + * @cssprop --umb-rte-width - The width of the rich-text-editor (default: unset) + * @cssprop --umb-rte-min-width - The minimum width of the rich-text-editor (default: unset) + * @cssprop --umb-rte-max-width - The maximum width of the rich-text-editor (default: 100%) + * @cssprop --umb-rte-height - The height of the rich-text-editor (default: 100%) + * @cssprop --umb-rte-min-height - The minimum height of the rich-text-editor (default: 100%) + * @cssprop --umb-rte-max-height - The maximum height of the rich-text-editor (default: 100%) + */ export abstract class UmbPropertyEditorUiRteElementBase extends UmbFormControlMixin(UmbLitElement) implements UmbPropertyEditorUiElement { + public name?: string; + public set config(config: UmbPropertyEditorConfigCollection | undefined) { if (!config) return; diff --git a/src/Umbraco.Web.UI.Client/src/packages/search/types.ts b/src/Umbraco.Web.UI.Client/src/packages/search/types.ts index 3a59d87d6b..664ca34f3a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/search/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/search/types.ts @@ -1,9 +1,6 @@ import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; -import type { - FieldPresentationModel, - SearchResultResponseModelReadable, -} from '@umbraco-cms/backoffice/external/backend-api'; +import type { FieldPresentationModel, SearchResultResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbPagedModel, UmbRepositoryResponse } from '@umbraco-cms/backoffice/repository'; export type { UmbSearchDataSource } from './search-data-source.interface.js'; @@ -12,7 +9,7 @@ export type { UmbSearchRepository } from './search-repository.interface.js'; export type * from './extensions/types.js'; export type * from './global-search/types.js'; -export type UmbSearchResultModel = SearchResultResponseModelReadable; +export type UmbSearchResultModel = SearchResultResponseModel; // TODO: lower requirement for search provider item type export type UmbSearchResultItemModel = { diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/constants.ts index 6afae28700..f582a679af 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/constants.ts @@ -1,5 +1,6 @@ export * from './entity-actions/constants.js'; +export * from './entity.js'; +export * from './menu/constants.js'; export * from './repository/constants.js'; export * from './tree/constants.js'; export * from './workspace/constants.js'; -export * from './entity.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/menu/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/menu/constants.ts new file mode 100644 index 0000000000..ad80a4635d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/menu/constants.ts @@ -0,0 +1 @@ +export const UMB_PARTIAL_VIEW_MENU_ITEM_ALIAS = 'Umb.MenuItem.PartialView'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/menu/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/menu/manifests.ts index 414875d9d6..7189714ccd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/menu/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/menu/manifests.ts @@ -1,11 +1,12 @@ import { UMB_PARTIAL_VIEW_TREE_ALIAS } from '../tree/index.js'; +import { UMB_PARTIAL_VIEW_MENU_ITEM_ALIAS } from './constants.js'; import { UMB_WORKSPACE_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace'; export const manifests: Array = [ { type: 'menuItem', kind: 'tree', - alias: 'Umb.MenuItem.PartialView', + alias: UMB_PARTIAL_VIEW_MENU_ITEM_ALIAS, name: 'Partial View Menu Item', weight: 40, meta: { @@ -16,9 +17,13 @@ export const manifests: Array = [ }, { type: 'workspaceContext', + kind: 'menuStructure', name: 'Partial View Menu Structure Workspace Context', alias: 'Umb.Context.PartialView.Menu.Structure', api: () => import('./partial-view-menu-structure.context.js'), + meta: { + menuItemAlias: UMB_PARTIAL_VIEW_MENU_ITEM_ALIAS, + }, conditions: [ { alias: UMB_WORKSPACE_CONDITION_ALIAS, diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/menu/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/menu/manifests.ts index ae46cc2ec9..b7960bb90d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/menu/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/menu/manifests.ts @@ -17,9 +17,13 @@ export const manifests: Array = [ }, { type: 'workspaceContext', + kind: 'menuStructure', name: 'Script Menu Structure Workspace Context', alias: 'Umb.Context.Script.Menu.Structure', api: () => import('./script-menu-structure.context.js'), + meta: { + menuItemAlias: UMB_SCRIPT_MENU_ITEM_ALIAS, + }, conditions: [ { alias: UMB_WORKSPACE_CONDITION_ALIAS, diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/constants.ts index cb3c3c7348..1b0f9b850a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/constants.ts @@ -1,5 +1,6 @@ export * from './entity-actions/constants.js'; export * from './global-components/constants.js'; +export * from './menu/constants.js'; export * from './repository/constants.js'; export * from './tree/constants.js'; export * from './workspace/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/menu/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/menu/constants.ts new file mode 100644 index 0000000000..ac1784982e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/menu/constants.ts @@ -0,0 +1 @@ +export const UMB_STYLESHEET_MENU_ITEM_ALIAS = 'Umb.MenuItem.Stylesheets'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/menu/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/menu/manifests.ts index 6e69c74258..b14e048c3b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/menu/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/menu/manifests.ts @@ -1,11 +1,12 @@ import { UMB_STYLESHEET_TREE_ALIAS } from '../constants.js'; +import { UMB_STYLESHEET_MENU_ITEM_ALIAS } from './constants.js'; import { UMB_WORKSPACE_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace'; export const manifests: Array = [ { type: 'menuItem', kind: 'tree', - alias: 'Umb.MenuItem.Stylesheets', + alias: UMB_STYLESHEET_MENU_ITEM_ALIAS, name: 'Stylesheets Menu Item', weight: 20, meta: { @@ -16,9 +17,13 @@ export const manifests: Array = [ }, { type: 'workspaceContext', + kind: 'menuStructure', name: 'Stylesheet Menu Structure Workspace Context', alias: 'Umb.Context.Stylesheet.Menu.Structure', api: () => import('./stylesheet-menu-structure.context.js'), + meta: { + menuItemAlias: UMB_STYLESHEET_MENU_ITEM_ALIAS, + }, conditions: [ { alias: UMB_WORKSPACE_CONDITION_ALIAS, diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/constants.ts index 9879ba5d2c..eac8bbbe48 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/constants.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/constants.ts @@ -1,4 +1,5 @@ export * from './conditions/constants.js'; +export * from './menu/constants.js'; export * from './repository/constants.js'; export * from './search/constants.js'; export * from './workspace/constants.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/global-components/input-template/input-template.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/global-components/input-template/input-template.element.ts index e1ba049486..03a1dba441 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/global-components/input-template/input-template.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/global-components/input-template/input-template.element.ts @@ -101,7 +101,7 @@ export class UmbInputTemplateElement extends UUIFormControlMixin(UmbLitElement, } protected override getFormElement() { - return this; + return undefined; } #appendTemplates(unique: string[]) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/menu/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/menu/constants.ts new file mode 100644 index 0000000000..0b25774bd4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/menu/constants.ts @@ -0,0 +1 @@ +export const UMB_TEMPLATE_MENU_ITEM_ALIAS = 'Umb.MenuItem.Templates'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/menu/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/menu/manifests.ts index 3c63d3fe2b..02f0e9dd3d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/menu/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/menu/manifests.ts @@ -1,11 +1,12 @@ import { UMB_TEMPLATE_TREE_ALIAS } from '../tree/index.js'; +import { UMB_TEMPLATE_MENU_ITEM_ALIAS } from './constants.js'; import { UMB_WORKSPACE_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace'; export const manifests: Array = [ { type: 'menuItem', kind: 'tree', - alias: 'Umb.MenuItem.Templates', + alias: UMB_TEMPLATE_MENU_ITEM_ALIAS, name: 'Templates Menu Item', weight: 40, meta: { @@ -17,9 +18,13 @@ export const manifests: Array = [ }, { type: 'workspaceContext', + kind: 'menuStructure', name: 'Template Menu Structure Workspace Context', alias: 'Umb.Context.Template.Menu.Structure', api: () => import('./template-menu-structure.context.js'), + meta: { + menuItemAlias: UMB_TEMPLATE_MENU_ITEM_ALIAS, + }, conditions: [ { alias: UMB_WORKSPACE_CONDITION_ALIAS, diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/anchor-modal/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/anchor-modal/manifests.ts deleted file mode 100644 index 99bbb4a2b5..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/anchor-modal/manifests.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { UMB_TIPTAP_ANCHOR_MODAL_ALIAS } from './constants.js'; -import type { ManifestModal } from '@umbraco-cms/backoffice/modal'; - -export const manifests: Array = [ - { - type: 'modal', - alias: UMB_TIPTAP_ANCHOR_MODAL_ALIAS, - name: 'Tiptap Anchor Modal', - element: () => import('./anchor-modal.element.js'), - }, -]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/cascading-menu-popover/index.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/cascading-menu-popover/index.ts new file mode 100644 index 0000000000..ec606adf12 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/cascading-menu-popover/index.ts @@ -0,0 +1 @@ +export * from './cascading-menu-popover.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/character-map/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/character-map/manifests.ts deleted file mode 100644 index a238e84eb3..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/character-map/manifests.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { UMB_TIPTAP_CHARACTER_MAP_MODAL_ALIAS } from './constants.js'; -import type { ManifestModal } from '@umbraco-cms/backoffice/modal'; - -export const manifests: Array = [ - { - type: 'modal', - alias: UMB_TIPTAP_CHARACTER_MAP_MODAL_ALIAS, - name: 'Character Map Modal', - element: () => import('./character-map-modal.element.js'), - }, -]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/index.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/index.ts index 56491b08d4..cc425ff2d0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/index.ts @@ -1,5 +1,3 @@ +export * from './cascading-menu-popover/index.js'; export * from './input-tiptap/index.js'; -export * from './anchor-modal/index.js'; -export * from './cascading-menu-popover/cascading-menu-popover.element.js'; -export * from './character-map/index.js'; export * from './menu/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts index 0bb9fdbc66..03ef568b09 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -21,8 +21,8 @@ import type { CSSResultGroup } from '@umbraco-cms/backoffice/external/lit'; import type { Extensions } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; -import './tiptap-toolbar.element.js'; -import './tiptap-statusbar.element.js'; +import '../toolbar/tiptap-toolbar.element.js'; +import '../statusbar/tiptap-statusbar.element.js'; const TIPTAP_CORE_EXTENSION_ALIAS = 'Umb.Tiptap.RichTextEssentials'; @@ -56,6 +56,9 @@ export class UmbInputTiptapElement extends UmbFormControlMixin stylesheet, - (stylesheet) => html``, + (stylesheet) => html``, )}