diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts index d292fc9be4..1dfb38b1c7 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts @@ -23,6 +23,7 @@ const CORE_PACKAGES = [ import('../../packages/media/umbraco-package.js'), import('../../packages/members/umbraco-package.js'), import('../../packages/models-builder/umbraco-package.js'), + import('../../packages/multi-url-picker/umbraco-package.js'), import('../../packages/packages/umbraco-package.js'), import('../../packages/property-editors/umbraco-package.js'), import('../../packages/relations/umbraco-package.js'), diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/index.ts index 7d6646a4f4..be212031a2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/index.ts @@ -17,7 +17,6 @@ export * from './input-dropdown/index.js'; export * from './input-entity/index.js'; export * from './input-eye-dropper/index.js'; export * from './input-manifest/index.js'; -export * from './input-multi-url/index.js'; export * from './input-number-range/index.js'; export * from './input-radio-button-list/index.js'; export * from './input-slider/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-multi-url/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-multi-url/index.ts deleted file mode 100644 index d197dc0c3d..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-multi-url/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './input-multi-url.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-multi-url/input-multi-url.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-multi-url/input-multi-url.stories.ts deleted file mode 100644 index 1017d7d56d..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-multi-url/input-multi-url.stories.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/web-components'; -import './input-multi-url.element.js'; -import type { UmbInputMultiUrlElement } from './input-multi-url.element.js'; - -const meta: Meta = { - title: 'Components/Inputs/Multi URL', - component: 'umb-input-multi-url', -}; - -export default meta; -type Story = StoryObj; - -export const Overview: Story = { - args: {}, -}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts index bdd6749492..f7b427da73 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts @@ -67,6 +67,7 @@ import type { ManifestSearchResultItem } from './search-result-item.model.js'; import type { ManifestAppEntryPoint } from './app-entry-point.model.js'; import type { ManifestBackofficeEntryPoint } from './backoffice-entry-point.model.js'; import type { ManifestEntryPoint } from './entry-point.model.js'; +import type { ManifestMonacoMarkdownEditorAction } from './monaco-markdown-editor-action.model.js'; import type { ManifestBase, ManifestBundle, ManifestCondition } from '@umbraco-cms/backoffice/extension-api'; export type * from './app-entry-point.model.js'; @@ -94,6 +95,7 @@ export type * from './menu-item.model.js'; export type * from './menu.model.js'; export type * from './mfa-login-provider.model.js'; export type * from './modal.model.js'; +export type * from './monaco-markdown-editor-action.model.js'; export type * from './package-view.model.js'; export type * from './property-action.model.js'; export type * from './property-editor.model.js'; @@ -179,6 +181,7 @@ export type ManifestTypes = | ManifestMenuItemTreeKind | ManifestMfaLoginProvider | ManifestModal + | ManifestMonacoMarkdownEditorAction | ManifestPackageView | ManifestPropertyActions | ManifestPropertyEditorSchema diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/monaco-markdown-editor-action.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/monaco-markdown-editor-action.model.ts new file mode 100644 index 0000000000..d836458a27 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/monaco-markdown-editor-action.model.ts @@ -0,0 +1,8 @@ +import type { ManifestApi } from '@umbraco-cms/backoffice/extension-api'; + +export interface ManifestMonacoMarkdownEditorAction extends ManifestApi { + type: 'monacoMarkdownEditorAction'; + meta?: MetaMonacoMarkdownEditorAction; +} + +export interface MetaMonacoMarkdownEditorAction {} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/manifests.ts index 12274ed8e4..e5308f533d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/manifests.ts @@ -13,12 +13,6 @@ const modals: Array = [ name: 'Icon Picker Modal', element: () => import('./icon-picker/icon-picker-modal.element.js'), }, - { - type: 'modal', - alias: 'Umb.Modal.LinkPicker', - name: 'Link Picker Modal', - element: () => import('./link-picker/link-picker-modal.element.js'), - }, { type: 'modal', alias: 'Umb.Modal.CodeEditor', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/index.ts index 4da1b3c794..a2958ea597 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/index.ts @@ -6,7 +6,6 @@ export * from './embedded-media-modal.token.js'; export * from './entity-user-permission-settings-modal.token.js'; export * from './icon-picker-modal.token.js'; export * from './item-picker-modal.token.js'; -export * from './link-picker-modal.token.js'; export * from './modal-token.js'; export * from './property-editor-ui-picker-modal.token.js'; export * from './workspace-modal.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/components/input-markdown-editor/input-markdown.element.ts b/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/components/input-markdown-editor/input-markdown.element.ts index e669b542af..005a9ada65 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/components/input-markdown-editor/input-markdown.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/components/input-markdown-editor/input-markdown.element.ts @@ -1,4 +1,13 @@ -import { css, html, customElement, query, property, unsafeHTML, when } from '@umbraco-cms/backoffice/external/lit'; +import { + css, + html, + customElement, + query, + property, + unsafeHTML, + when, + state, +} from '@umbraco-cms/backoffice/external/lit'; import { DOMPurify } from '@umbraco-cms/backoffice/external/dompurify'; import { marked } from '@umbraco-cms/backoffice/external/marked'; import { monaco } from '@umbraco-cms/backoffice/external/monaco-editor'; @@ -7,12 +16,14 @@ import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app'; -import { UMB_LINK_PICKER_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import { UMB_MEDIA_TREE_PICKER_MODAL } from '@umbraco-cms/backoffice/media'; import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; import type { UmbCodeEditorController, UmbCodeEditorElement } from '@umbraco-cms/backoffice/code-editor'; import type { UmbModalManagerContext } from '@umbraco-cms/backoffice/modal'; import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { createExtensionApi } from '@umbraco-cms/backoffice/extension-api'; /** * @element umb-input-markdown @@ -39,6 +50,9 @@ export class UmbInputMarkdownElement extends UUIFormControlMixin(UmbLitElement, @query('umb-code-editor') _codeEditor?: UmbCodeEditorElement; + @state() + _actionExtensions: Array = []; + private _modalContext?: UmbModalManagerContext; private serverUrl?: string; @@ -46,9 +60,11 @@ export class UmbInputMarkdownElement extends UUIFormControlMixin(UmbLitElement, constructor() { super(); this.#loadCodeEditor(); + this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (instance) => { this._modalContext = instance; }); + this.consumeContext(UMB_APP_CONTEXT, (instance) => { this.serverUrl = instance.getServerUrl(); }); @@ -67,6 +83,23 @@ export class UmbInputMarkdownElement extends UUIFormControlMixin(UmbLitElement, }); // Prefer to update options before showing the editor, to avoid seeing the changes in the UI. this.#isCodeEditorReady.setValue(true); + + // TODO: make all action into extensions + this.observe(umbExtensionsRegistry.byType('monacoMarkdownEditorAction'), (manifests) => { + manifests.forEach(async (manifest) => { + const api = await createExtensionApi(this, manifest, [this]); + const action: monaco.editor.IActionDescriptor = { + id: api.getUnique(), + label: api.getLabel(), + keybindings: api.getKeybindings(), + run: async () => await api.execute({ editor: this.#editor }), + }; + this.#editor?.monacoEditor?.addAction(action); + this._actionExtensions.push(action); + this.requestUpdate('_actionExtensions'); + }); + }); + this.#loadActions(); } catch (error) { console.error(error); @@ -157,12 +190,6 @@ export class UmbInputMarkdownElement extends UUIFormControlMixin(UmbLitElement, id: 'line', run: () => this._insertLine(), }); - this.#editor?.monacoEditor?.addAction({ - label: 'Add Link', - id: 'link', - keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK], - run: () => this._insertLink(), - }); this.#editor?.monacoEditor?.addAction({ label: 'Add Image', id: 'image', @@ -172,61 +199,18 @@ export class UmbInputMarkdownElement extends UUIFormControlMixin(UmbLitElement, }); } + #onActionClick(event: any, action: monaco.editor.IActionDescriptor) { + event.stopPropagation(); + const hasAction = this.#editor?.monacoEditor?.getAction(action.id); + if (!hasAction) throw new Error(`Action ${action.id} not found in the editor.`); + this.#editor?.monacoEditor?.getAction(action.id)?.run(); + } + private _focusEditor(): void { // If we press one of the action buttons manually (which is outside the editor), we need to focus the editor again. this.#editor?.monacoEditor?.focus(); } - private _insertLink() { - const selection = this.#editor?.getSelections()[0]; - if (!selection || !this._modalContext) return; - - const selectedValue = this.#editor?.getValueInRange(selection); - - this._focusEditor(); // Focus before opening modal - const modalContext = this._modalContext.open(this, UMB_LINK_PICKER_MODAL, { - modal: { size: this.overlaySize }, - data: { - index: null, - config: {}, - }, - value: { - link: { name: selectedValue }, - }, - }); - - modalContext - ?.onSubmit() - .then((value) => { - if (!value) return; - - const name = this.localize.term('general_name'); - const url = this.localize.term('general_url'); - - this.#editor?.monacoEditor?.executeEdits('', [ - { range: selection, text: `[${value.link.name || name}](${value.link.url || url})` }, - ]); - - if (!value.link.name) { - this.#editor?.select({ - startColumn: selection.startColumn + 1, - endColumn: selection.startColumn + 1 + name.length, - endLineNumber: selection.startLineNumber, - startLineNumber: selection.startLineNumber, - }); - } else if (!value.link.url) { - this.#editor?.select({ - startColumn: selection.startColumn + 3 + value.link.name.length, - endColumn: selection.startColumn + 3 + value.link.name.length + url.length, - endLineNumber: selection.startLineNumber, - startLineNumber: selection.startLineNumber, - }); - } - }) - .catch(() => undefined) - .finally(() => this._focusEditor()); - } - private _insertMedia() { const selection = this.#editor?.getSelections()[0]; if (!selection) return; @@ -488,14 +472,6 @@ export class UmbInputMarkdownElement extends UUIFormControlMixin(UmbLitElement, @click=${() => this.#editor?.monacoEditor?.getAction('line')?.run()}> - this.#editor?.monacoEditor?.getAction('link')?.run()}> - - this.#editor?.monacoEditor?.getAction('image')?.run()}> + + ${this._actionExtensions.map( + (action) => html` + this.#onActionClick(event, action)}> + + + `, + )}
( - 'Umb.Modal.LinkPicker', + UMB_MULTI_URL_PICKER_MODAL_ALIAS, { modal: { type: 'sidebar', diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/link-picker-modal/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/link-picker-modal/manifests.ts new file mode 100644 index 0000000000..b36c62f997 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/link-picker-modal/manifests.ts @@ -0,0 +1,10 @@ +import { UMB_MULTI_URL_PICKER_MODAL_ALIAS } from './constants.js'; + +export const manifests = [ + { + type: 'modal', + alias: UMB_MULTI_URL_PICKER_MODAL_ALIAS, + name: 'Property Editor Multi Url Link Picker Modal', + element: () => import('./link-picker-modal.element.js'), + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/link-picker-modal/types.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/link-picker-modal/types.ts new file mode 100644 index 0000000000..3f1775721a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/link-picker-modal/types.ts @@ -0,0 +1,13 @@ +export type UmbLinkPickerLinkType = 'document' | 'external' | 'media'; + +export interface UmbLinkPickerLink { + icon?: string | null; + name?: string | null; + published?: boolean | null; + queryString?: string | null; + target?: string | null; + trashed?: boolean | null; + type?: UmbLinkPickerLinkType | null; + unique?: string | null; + url?: string | null; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/manifests.ts new file mode 100644 index 0000000000..d8c3833b2e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/manifests.ts @@ -0,0 +1,12 @@ +import { manifests as modalManifests } from './link-picker-modal/manifests.js'; +import { manifests as monacoMarkdownEditorManifests } from './monaco-markdown-editor-action/manifests.js'; +import { manifests as propertyEditorManifests } from './property-editor/manifests.js'; +import { manifests as tinyMceManifests } from './tiny-mce-plugin/manifests.js'; +import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifests: Array = [ + ...modalManifests, + ...monacoMarkdownEditorManifests, + ...propertyEditorManifests, + ...tinyMceManifests, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/monaco-markdown-editor-action/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/monaco-markdown-editor-action/manifests.ts new file mode 100644 index 0000000000..78748548a1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/monaco-markdown-editor-action/manifests.ts @@ -0,0 +1,8 @@ +export const manifests = [ + { + type: 'monacoMarkdownEditorAction', + alias: 'Umb.MonacoMarkdownEditorAction.MultiUrlPicker', + name: 'Multi Url Picker Monaco Markdown Editor Action', + js: () => import('./url-picker-monaco-markdown-editor-action.js'), + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/monaco-markdown-editor-action/url-picker-monaco-markdown-editor-action.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/monaco-markdown-editor-action/url-picker-monaco-markdown-editor-action.ts new file mode 100644 index 0000000000..061dd98255 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/monaco-markdown-editor-action/url-picker-monaco-markdown-editor-action.ts @@ -0,0 +1,83 @@ +import { UMB_LINK_PICKER_MODAL } from '../link-picker-modal/link-picker-modal.token.js'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { monaco } from '@umbraco-cms/backoffice/external/monaco-editor'; +import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui'; +import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; + +export class UmbUrlPickerMonacoMarkdownEditorAction extends UmbControllerBase { + #localize = new UmbLocalizationController(this); + + constructor(host: UmbControllerHost) { + super(host); + } + + getUnique() { + return 'Umb.MonacoMarkdownEditorAction.UrlPicker'; + } + + getLabel() { + return this.#localize.term('general_insertLink'); + } + + getKeybindings() { + return [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK]; + } + + async execute({ editor, overlaySize }: { editor: any; overlaySize: UUIModalSidebarSize }) { + if (!editor) throw new Error('Editor not found'); + const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + if (!modalManager) throw new Error('Modal manager not found'); + + const selection = editor?.getSelections()[0]; + if (!selection) return; + + const selectedValue = editor?.getValueInRange(selection); + editor.monacoEditor?.focus(); + + const modalContext = modalManager.open(this, UMB_LINK_PICKER_MODAL, { + modal: { size: overlaySize }, + data: { + index: null, + config: {}, + }, + value: { + link: { name: selectedValue }, + }, + }); + + modalContext + ?.onSubmit() + .then((value) => { + if (!value) return; + + const name = this.#localize.term('general_name'); + const url = this.#localize.term('general_url'); + + editor.monacoEditor?.executeEdits('', [ + { range: selection, text: `[${value.link.name || name}](${value.link.url || url})` }, + ]); + + if (!value.link.name) { + editor.select({ + startColumn: selection.startColumn + 1, + endColumn: selection.startColumn + 1 + name.length, + endLineNumber: selection.startLineNumber, + startLineNumber: selection.startLineNumber, + }); + } else if (!value.link.url) { + editor.select({ + startColumn: selection.startColumn + 3 + value.link.name.length, + endColumn: selection.startColumn + 3 + value.link.name.length + url.length, + endLineNumber: selection.startLineNumber, + startLineNumber: selection.startLineNumber, + }); + } + }) + .catch(() => undefined) + .finally(() => editor.monacoEditor?.focus()); + } +} + +export { UmbUrlPickerMonacoMarkdownEditorAction as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/index.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/index.ts new file mode 100644 index 0000000000..2d2ffcb054 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/index.ts @@ -0,0 +1 @@ +export * from './multi-url-picker.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-multi-url/input-multi-url.element.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/multi-url-picker.element.ts similarity index 93% rename from src/Umbraco.Web.UI.Client/src/packages/core/components/input-multi-url/input-multi-url.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/multi-url-picker.element.ts index 1a9e9f3082..5600821f11 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-multi-url/input-multi-url.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/multi-url-picker.element.ts @@ -1,15 +1,13 @@ +import type { UmbLinkPickerLink } from '../link-picker-modal/types.js'; +import { UMB_LINK_PICKER_MODAL } from '../link-picker-modal/link-picker-modal.token.js'; import { css, customElement, html, property, repeat, state } from '@umbraco-cms/backoffice/external/lit'; import { simpleHashCode } from '@umbraco-cms/backoffice/observable-api'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { - umbConfirmModal, - UmbModalRouteRegistrationController, - UMB_LINK_PICKER_MODAL, -} from '@umbraco-cms/backoffice/modal'; +import { umbConfirmModal, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; -import type { UmbModalRouteBuilder, UmbLinkPickerLink } from '@umbraco-cms/backoffice/modal'; +import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/modal'; import type { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui'; @@ -19,8 +17,9 @@ import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui'; * @fires blur - when the input loses focus * @fires focus - when the input gains focus */ -@customElement('umb-input-multi-url') -export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement, '') { +const elementName = 'umb-multi-url-picker'; +@customElement(elementName) +export class UmbMultiUrlPickerElement extends UUIFormControlMixin(UmbLitElement, '') { #sorter = new UmbSorterController(this, { getUniqueOfElement: (element) => { return element.id; @@ -300,6 +299,6 @@ export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement, declare global { interface HTMLElementTagNameMap { - 'umb-input-multi-url': UmbInputMultiUrlElement; + [elementName]: UmbMultiUrlPickerElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/multi-url-picker.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/multi-url-picker.stories.ts new file mode 100644 index 0000000000..2772b6ccd2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/multi-url-picker/multi-url-picker.stories.ts @@ -0,0 +1,15 @@ +import type { Meta, StoryObj } from '@storybook/web-components'; +import './multi-url-picker.element.js'; +import type { UmbMultiUrlPickerElement } from './multi-url-picker.element.js'; + +const meta: Meta = { + title: 'Components/Inputs/Multi URL', + component: 'umb-input-multi-url', +}; + +export default meta; +type Story = StoryObj; + +export const Overview: Story = { + args: {}, +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/multi-url-picker/Umbraco.MultiUrlPicker.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/Umbraco.MultiUrlPicker.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/property-editors/multi-url-picker/Umbraco.MultiUrlPicker.ts rename to src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/Umbraco.MultiUrlPicker.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/manifests.ts new file mode 100644 index 0000000000..cfab088c70 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/manifests.ts @@ -0,0 +1,33 @@ +import { manifest as schemaManifest } from './Umbraco.MultiUrlPicker.js'; + +export const manifests = [ + { + type: 'propertyEditorUi', + alias: 'Umb.PropertyEditorUi.MultiUrlPicker', + name: 'Multi URL Picker Property Editor UI', + element: () => import('./property-editor-ui-multi-url-picker.element.js'), + meta: { + label: 'Multi URL Picker', + propertyEditorSchemaAlias: 'Umbraco.MultiUrlPicker', + icon: 'icon-link', + group: 'pickers', + settings: { + properties: [ + { + alias: 'overlaySize', + label: 'Overlay Size', + description: 'Select the width of the overlay.', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.OverlaySize', + }, + { + alias: 'hideAnchor', + label: 'Hide anchor/query string input', + description: 'Selecting this hides the anchor/query string input field in the link picker overlay.', + propertyEditorUiAlias: 'Umb.PropertyEditorUi.Toggle', + }, + ], + }, + }, + }, + schemaManifest, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/multi-url-picker/property-editor-ui-multi-url-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/property-editor-ui-multi-url-picker.element.ts similarity index 87% rename from src/Umbraco.Web.UI.Client/src/packages/property-editors/multi-url-picker/property-editor-ui-multi-url-picker.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/property-editor-ui-multi-url-picker.element.ts index 9b21b2be37..ee10b18853 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/multi-url-picker/property-editor-ui-multi-url-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/property-editor-ui-multi-url-picker.element.ts @@ -1,13 +1,16 @@ +import type { UmbLinkPickerLink } from '../link-picker-modal/types.js'; +import type { UmbMultiUrlPickerElement } from '../multi-url-picker/multi-url-picker.element.js'; import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property'; -import type { UmbInputMultiUrlElement } from '@umbraco-cms/backoffice/components'; -import type { UmbLinkPickerLink } from '@umbraco-cms/backoffice/modal'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui'; +// import of local component +import '../multi-url-picker/multi-url-picker.element.js'; + /** * @element umb-property-editor-ui-multi-url-picker */ @@ -56,14 +59,14 @@ export class UmbPropertyEditorUIMultiUrlPickerElement extends UmbLitElement impl }); } - #onChange(event: CustomEvent & { target: UmbInputMultiUrlElement }) { + #onChange(event: CustomEvent & { target: UmbMultiUrlPickerElement }) { this.value = event.target.urls; this.dispatchEvent(new UmbPropertyValueChangeEvent()); } render() { return html` - - + `; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/multi-url-picker/property-editor-ui-multi-url-picker.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/property-editor-ui-multi-url-picker.stories.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/property-editors/multi-url-picker/property-editor-ui-multi-url-picker.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/property-editor-ui-multi-url-picker.stories.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/multi-url-picker/property-editor-ui-multi-url-picker.test.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/property-editor-ui-multi-url-picker.test.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/property-editors/multi-url-picker/property-editor-ui-multi-url-picker.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/property-editor-ui-multi-url-picker.test.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/tiny-mce-plugin/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/tiny-mce-plugin/manifests.ts new file mode 100644 index 0000000000..8d7455f5e5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/tiny-mce-plugin/manifests.ts @@ -0,0 +1,22 @@ +export const manifests = [ + { + type: 'tinyMcePlugin', + alias: 'Umb.TinyMcePlugin.MultiUrlPicker', + name: 'Multi Url Picker TinyMCE Plugin', + js: () => import('./tiny-mce-multi-url-picker.plugin.js'), + meta: { + toolbar: [ + { + alias: 'link', + label: 'Insert/Edit link', + icon: 'link', + }, + { + alias: 'unlink', + label: 'Remove link', + icon: 'unlink', + }, + ], + }, + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-linkpicker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/tiny-mce-plugin/tiny-mce-multi-url-picker.plugin.ts similarity index 91% rename from src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-linkpicker.plugin.ts rename to src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/tiny-mce-plugin/tiny-mce-multi-url-picker.plugin.ts index 0536f0347e..2f6e182997 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-linkpicker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/tiny-mce-plugin/tiny-mce-multi-url-picker.plugin.ts @@ -1,6 +1,8 @@ -import { type TinyMcePluginArguments, UmbTinyMcePluginBase } from '../components/input-tiny-mce/tiny-mce-plugin.js'; -import type { UmbLinkPickerModalValue, UmbLinkPickerLink } from '@umbraco-cms/backoffice/modal'; -import { UMB_LINK_PICKER_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import type { UmbLinkPickerModalValue } from '../link-picker-modal/link-picker-modal.token.js'; +import { UMB_LINK_PICKER_MODAL } from '../link-picker-modal/link-picker-modal.token.js'; +import type { UmbLinkPickerLink } from '../link-picker-modal/types.js'; +import { type TinyMcePluginArguments, UmbTinyMcePluginBase } from '@umbraco-cms/backoffice/tiny-mce'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; type AnchorElementAttributes = { href?: string | null; @@ -11,7 +13,7 @@ type AnchorElementAttributes = { text?: string; }; -export default class UmbTinyMceLinkPickerPlugin extends UmbTinyMcePluginBase { +export default class UmbTinyMceMultiUrlPickerPlugin extends UmbTinyMcePluginBase { #linkPickerData?: UmbLinkPickerModalValue; #anchorElement?: HTMLAnchorElement; @@ -72,7 +74,7 @@ export default class UmbTinyMceLinkPickerPlugin extends UmbTinyMcePluginBase { if (this.#anchorElement.href.includes('localLink:')) { const href = this.#anchorElement.getAttribute('href')!; - currentTarget.unique = href.substring(href.indexOf(":") + 1, href.indexOf("}")); + currentTarget.unique = href.substring(href.indexOf(':') + 1, href.indexOf('}')); } else if (this.#anchorElement.host.length) { currentTarget.url = this.#anchorElement.protocol ? this.#anchorElement.protocol + '//' : undefined; currentTarget.url += this.#anchorElement.host + this.#anchorElement.pathname; @@ -122,9 +124,10 @@ export default class UmbTinyMceLinkPickerPlugin extends UmbTinyMcePluginBase { a.title = name; } - if (this.#linkPickerData?.link.queryString?.startsWith('#') || - this.#linkPickerData?.link.queryString?.startsWith('?')) - { + if ( + this.#linkPickerData?.link.queryString?.startsWith('#') || + this.#linkPickerData?.link.queryString?.startsWith('?') + ) { a['data-anchor'] = this.#linkPickerData?.link.queryString; a.href += this.#linkPickerData?.link.queryString; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/umbraco-package.ts new file mode 100644 index 0000000000..85548e535c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/umbraco-package.ts @@ -0,0 +1,9 @@ +export const name = 'Umbraco.Core.MultiUrlPicker'; +export const extensions = [ + { + name: 'Multi Url Picker Bundle', + alias: 'Umb.Bundle.MultiUrlPicker', + type: 'bundle', + js: () => import('./manifests.js'), + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/collection/legacy-property-editor-ui-collection.element.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/collection/legacy-property-editor-ui-collection.element.ts deleted file mode 100644 index 175d2acf2a..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/collection/legacy-property-editor-ui-collection.element.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { UmbPropertyEditorUICollectionElement } from './property-editor-ui-collection.element.js'; -import { customElement } from '@umbraco-cms/backoffice/external/lit'; - -/** - * @element umb-legacy-property-editor-ui-collection - * @deprecated Use "umb-property-editor-ui-collection" instead. - */ -@customElement('umb-legacy-property-editor-ui-collection') -export class UmbLegacyPropertyEditorUICollectionElement extends UmbPropertyEditorUICollectionElement { - constructor() { - super(); - console.warn( - 'The element "umb-legacy-property-editor-ui-collection" has been deprecated. Use "umb-property-editor-ui-collection" instead.', - ); - } -} - -export default UmbPropertyEditorUICollectionElement; - -declare global { - interface HTMLElementTagNameMap { - 'umb-legacy-property-editor-ui-collection': UmbPropertyEditorUICollectionElement; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/collection/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/collection/manifests.ts index 942082d3d5..7dfe94e083 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/collection/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/collection/manifests.ts @@ -101,26 +101,11 @@ const propertyEditorUiManifest: ManifestPropertyEditorUi = { }, }; -/** - * Legacy property editor UI manifest for the collection view property editor. - * @deprecated Use the property editor UI alias of 'Umb.PropertyEditorUi.Collection' instead. - */ -const legacyPropertyEditorUiManifest: ManifestPropertyEditorUi = { - ...propertyEditorUiManifest, - alias: 'Umb.PropertyEditorUi.CollectionView', - element: () => import('./legacy-property-editor-ui-collection.element.js'), -}; - -const config: Array = [ +export const manifests: Array = [ + propertyEditorUiManifest, bulkActionPermissions, columnConfiguration, layoutConfiguration, orderBy, -]; - -export const manifests: Array = [ - propertyEditorUiManifest, - legacyPropertyEditorUiManifest, - ...config, schema, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/manifests.ts index 88606f14eb..6c8284da09 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/manifests.ts @@ -13,7 +13,6 @@ import { manifests as eyeDropperManifests } from './eye-dropper/manifests.js'; import { manifests as iconPickerManifests } from './icon-picker/manifests.js'; import { manifests as labelManifests } from './label/manifests.js'; import { manifests as multipleTextStringManifests } from './multiple-text-string/manifests.js'; -import { manifests as multiUrlPickerManifests } from './multi-url-picker/manifests.js'; import { manifests as numberManifests } from './number/manifests.js'; import { manifests as radioButtonListManifests } from './radio-button-list/manifests.js'; import { manifests as sliderManifests } from './slider/manifests.js'; @@ -34,7 +33,6 @@ export const manifests: Array = [ ...iconPickerManifests, ...labelManifests, ...multipleTextStringManifests, - ...multiUrlPickerManifests, ...numberManifests, ...radioButtonListManifests, ...sliderManifests, diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/multi-url-picker/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/multi-url-picker/manifests.ts deleted file mode 100644 index f82f93e2f3..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/multi-url-picker/manifests.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { manifest as schemaManifest } from './Umbraco.MultiUrlPicker.js'; -import type { ManifestPropertyEditorUi, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; - -const manifest: ManifestPropertyEditorUi = { - type: 'propertyEditorUi', - alias: 'Umb.PropertyEditorUi.MultiUrlPicker', - name: 'Multi URL Picker Property Editor UI', - element: () => import('./property-editor-ui-multi-url-picker.element.js'), - meta: { - label: 'Multi URL Picker', - propertyEditorSchemaAlias: 'Umbraco.MultiUrlPicker', - icon: 'icon-link', - group: 'pickers', - settings: { - properties: [ - { - alias: 'overlaySize', - label: 'Overlay Size', - description: 'Select the width of the overlay.', - propertyEditorUiAlias: 'Umb.PropertyEditorUi.OverlaySize', - }, - { - alias: 'hideAnchor', - label: 'Hide anchor/query string input', - description: 'Selecting this hides the anchor/query string input field in the link picker overlay.', - propertyEditorUiAlias: 'Umb.PropertyEditorUi.Toggle', - }, - ], - }, - }, -}; - -export const manifests: Array = [manifest, schemaManifest]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/manifests.ts index 56a1167d33..e70a612d5e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/manifests.ts @@ -16,26 +16,6 @@ export const manifests: Array = [ ], }, }, - { - type: 'tinyMcePlugin', - alias: 'Umb.TinyMcePlugin.LinkPicker', - name: 'Link Picker TinyMCE Plugin', - js: () => import('./tiny-mce-linkpicker.plugin.js'), - meta: { - toolbar: [ - { - alias: 'link', - label: 'Insert/Edit link', - icon: 'link', - }, - { - alias: 'unlink', - label: 'Remove link', - icon: 'unlink', - }, - ], - }, - }, { type: 'tinyMcePlugin', alias: 'Umb.TinyMcePlugin.MediaPicker',