Merge branch 'main' into chore/experimental-themes

This commit is contained in:
Mads Rasmussen
2024-05-15 14:48:15 +02:00
73 changed files with 617 additions and 466 deletions

View File

@@ -86,7 +86,7 @@ export class UmbAppElement extends UmbLitElement {
: this.localize.term('errors_externalLoginFailed');
this.observe(this.#authContext.authorizationSignal, () => {
window.location.href = '/';
history.replaceState(null, '', '');
});
}

View File

@@ -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'),

View File

@@ -1244,8 +1244,8 @@ export default {
openMediaPicker: 'Open media picker',
},
propertyEditorPicker: {
title: 'Select Property Editor',
openPropertyEditorPicker: 'Select Property Editor',
title: 'Select a property editor',
openPropertyEditorPicker: 'Select a property editor UI',
},
relatedlinks: {
enterExternal: 'enter external link',

View File

@@ -2543,8 +2543,8 @@ export default {
searchResults: 'items returned',
},
propertyEditorPicker: {
title: 'Select Property Editor',
openPropertyEditorPicker: 'Select Property Editor',
title: 'Select a property editor',
openPropertyEditorPicker: 'Select a property editor UI',
},
analytics: {
consentForAnalytics: 'Consent for telemetry data',

View File

@@ -6,8 +6,6 @@ export * from './body-layout/body-layout.element.js';
export * from './code-block/index.js';
export * from './dropdown/index.js';
export * from './entity-actions-bundle/index.js';
export * from './extension-slot/index.js';
export * from './extension-with-api-slot/index.js';
export * from './footer-layout/index.js';
export * from './header-app/index.js';
export * from './history/index.js';
@@ -19,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';

View File

@@ -29,6 +29,7 @@ export class UmbInputEntityElement extends UUIFormControlMixin(UmbLitElement, ''
protected getFormElement() {
return undefined;
}
@property({ type: Number })
public set min(value: number) {
this.#min = value;

View File

@@ -1 +0,0 @@
export * from './input-multi-url.element.js';

View File

@@ -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<UmbInputMultiUrlElement> = {
title: 'Components/Inputs/Multi URL',
component: 'umb-input-multi-url',
};
export default meta;
type Story = StoryObj<UmbInputMultiUrlElement>;
export const Overview: Story = {
args: {},
};

View File

@@ -1,8 +1,8 @@
import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit';
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { UUISliderEvent } from '@umbraco-cms/backoffice/external/uui';
import { customElement, html, property } from '@umbraco-cms/backoffice/external/lit';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
import type { UUISliderEvent } from '@umbraco-cms/backoffice/external/uui';
@customElement('umb-input-slider')
export class UmbInputSliderElement extends UUIFormControlMixin(UmbLitElement, '') {
@@ -28,9 +28,9 @@ export class UmbInputSliderElement extends UUIFormControlMixin(UmbLitElement, ''
return undefined;
}
#onChange(e: UUISliderEvent) {
e.stopPropagation();
this.value = e.target.value as string;
#onChange(event: UUISliderEvent) {
event.stopPropagation();
this.value = event.target.value as string;
this.dispatchEvent(new UmbChangeEvent());
}
@@ -39,20 +39,27 @@ export class UmbInputSliderElement extends UUIFormControlMixin(UmbLitElement, ''
}
#renderSlider() {
return html`<uui-slider
.min="${this.min}"
.max="${this.max}"
.step="${this.step}"
.value="${this.valueLow.toString()}"
@change="${this.#onChange}"></uui-slider>`;
return html`
<uui-slider
.min=${this.min}
.max=${this.max}
.step=${this.step}
.value=${this.valueLow.toString()}
@change=${this.#onChange}>
</uui-slider>
`;
}
#renderRangeSlider() {
return html`<uui-range-slider
.min="${this.min}"
.max="${this.max}"
.step="${this.step}"
.value="${this.valueLow},${this.valueHigh}"
@change="${this.#onChange}"></uui-range-slider>`;
return html`
<uui-range-slider
.min=${this.min}
.max=${this.max}
.step=${this.step}
.value="${this.valueLow},${this.valueHigh}"
@change=${this.#onChange}>
</uui-range-slider>
`;
}
}

View File

@@ -1,5 +1,5 @@
import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element";
import { customElement, html, css, property, classMap } from "@umbraco-cms/backoffice/external/lit";
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { classMap, customElement, css, html, property } from '@umbraco-cms/backoffice/external/lit';
/**
* @element umb-stack
@@ -7,72 +7,73 @@ import { customElement, html, css, property, classMap } from "@umbraco-cms/backo
* @extends LitElement
*/
@customElement('umb-stack')
export class UmbStackElement extends UmbLitElement
{
/**
* Look
* @type {String}
* @memberof UmbStackElement
*/
@property({ type:String })
look: 'compact' | 'default' = 'default';
export class UmbStackElement extends UmbLitElement {
/**
* Look
* @type {String}
* @memberof UmbStackElement
*/
@property({ type: String })
look: 'compact' | 'default' = 'default';
/**
* Divide
* @type {Boolean}
* @memberof UmbStackElement
*/
@property({ type:Boolean })
divide: boolean = false;
/**
* Divide
* @type {Boolean}
* @memberof UmbStackElement
*/
@property({ type: Boolean })
divide: boolean = false;
render() {
return html`<div class=${classMap({ divide: this.divide, compact: this.look === 'compact' })}>
<slot></slot>
</div>`;
}
render() {
return html`
<div class=${classMap({ divide: this.divide, compact: this.look === 'compact' })}>
<slot></slot>
</div>
`;
}
static styles = [
css`
div {
display: block;
position: relative;
}
static styles = [
css`
div {
display: block;
position: relative;
}
::slotted(*) {
position: relative;
margin-top: var(--uui-size-space-6);
}
::slotted(*) {
position: relative;
margin-top: var(--uui-size-space-6);
}
.divide ::slotted(*)::before {
content: '';
position: absolute;
top: calc((var(--uui-size-space-6) / 2) * -1);
height: 0;
width: 100%;
border-top: solid 1px var(--uui-color-divider-standalone);
}
.divide ::slotted(*)::before {
content: '';
position: absolute;
top: calc((var(--uui-size-space-6) / 2) * -1);
height: 0;
width: 100%;
border-top: solid 1px var(--uui-color-divider-standalone);
}
::slotted(*:first-child) {
margin-top: 0;
}
::slotted(*:first-child) {
margin-top: 0;
}
.divide ::slotted(*:first-child)::before {
display: none;
}
.divide ::slotted(*:first-child)::before {
display: none;
}
.compact ::slotted(*) {
margin-top: var(--uui-size-space-3);
}
.compact ::slotted(*) {
margin-top: var(--uui-size-space-3);
}
.compact ::slotted(*:first-child) {
margin-top: 0;
}
.compact ::slotted(*:first-child) {
margin-top: 0;
}
.compact.divide ::slotted(*)::before {
display: none;
}
`
];
.compact.divide ::slotted(*)::before {
display: none;
}
`,
];
}
export default UmbStackElement;

View File

@@ -8,6 +8,7 @@ import { UmbExtensionsApiInitializer, type UmbEntryPointOnInit } from '@umbraco-
// TODO temp relative import until modules ship a component sub module
import './menu/components/index.js';
import './extension-registry/components/index.js';
export const onInit: UmbEntryPointOnInit = (host, extensionRegistry) => {
new UmbExtensionsApiInitializer(host, extensionRegistry, 'globalContext', [host]);

View File

@@ -1,4 +1,4 @@
import { umbExtensionsRegistry } from '../../extension-registry/index.js';
import { umbExtensionsRegistry } from '../../registry.js';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { TemplateResult } from '@umbraco-cms/backoffice/external/lit';
import { css, repeat, customElement, property, state, html } from '@umbraco-cms/backoffice/external/lit';

View File

@@ -1,4 +1,4 @@
import { umbExtensionsRegistry } from '../../extension-registry/index.js';
import { umbExtensionsRegistry } from '../../registry.js';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { TemplateResult } from '@umbraco-cms/backoffice/external/lit';
import { css, repeat, customElement, property, state, html } from '@umbraco-cms/backoffice/external/lit';

View File

@@ -0,0 +1,2 @@
export * from './extension-slot/index.js';
export * from './extension-with-api-slot/index.js';

View File

@@ -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

View File

@@ -1,7 +1,10 @@
import type { UmbMenuItemElement } from '../interfaces/menu-item-element.interface.js';
import type { ManifestElement } from '@umbraco-cms/backoffice/extension-api';
import type { ConditionTypes } from '../conditions/types.js';
import type { ManifestWithDynamicConditions, ManifestElement } from '@umbraco-cms/backoffice/extension-api';
export interface ManifestMenuItem extends ManifestElement<UmbMenuItemElement> {
export interface ManifestMenuItem
extends ManifestElement<UmbMenuItemElement>,
ManifestWithDynamicConditions<ConditionTypes> {
type: 'menuItem';
meta: MetaMenuItem;
}

View File

@@ -0,0 +1,8 @@
import type { ManifestApi } from '@umbraco-cms/backoffice/extension-api';
export interface ManifestMonacoMarkdownEditorAction extends ManifestApi<any> {
type: 'monacoMarkdownEditorAction';
meta?: MetaMonacoMarkdownEditorAction;
}
export interface MetaMonacoMarkdownEditorAction {}

View File

@@ -1,13 +1,11 @@
import type { UUIColorSwatchesEvent } from '@umbraco-cms/backoffice/external/uui';
import { css, html, customElement, state, repeat, query, nothing } from '@umbraco-cms/backoffice/external/lit';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import type { UmbIconPickerModalData, UmbIconPickerModalValue } from '@umbraco-cms/backoffice/modal';
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
import { css, customElement, html, nothing, query, repeat, state } from '@umbraco-cms/backoffice/external/lit';
import { extractUmbColorVariable, umbracoColors } from '@umbraco-cms/backoffice/resources';
import { umbFocus } from '@umbraco-cms/backoffice/lit-element';
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UMB_ICON_REGISTRY_CONTEXT, type UmbIconDefinition } from '@umbraco-cms/backoffice/icon';
import type { UmbIconPickerModalData, UmbIconPickerModalValue } from '@umbraco-cms/backoffice/modal';
import type { UUIColorSwatchesEvent } from '@umbraco-cms/backoffice/external/uui';
@customElement('umb-icon-picker-modal')
export class UmbIconPickerModalElement extends UmbModalBaseElement<UmbIconPickerModalData, UmbIconPickerModalValue> {
@@ -106,12 +104,12 @@ export class UmbIconPickerModalElement extends UmbModalBaseElement<UmbIconPicker
<uui-button
slot="actions"
label=${this.localize.term('general_close')}
@click="${this._rejectModal}"></uui-button>
@click=${this._rejectModal}></uui-button>
<uui-button
slot="actions"
color="positive"
look="primary"
@click="${this._submitModal}"
@click=${this._submitModal}
label=${this.localize.term('general_submit')}></uui-button>
</umb-body-layout>
`;
@@ -123,7 +121,7 @@ export class UmbIconPickerModalElement extends UmbModalBaseElement<UmbIconPicker
placeholder=${this.localize.term('placeholders_filter')}
label=${this.localize.term('placeholders_filter')}
id="search"
@keyup="${this.#filterIcons}"
@keyup=${this.#filterIcons}
${umbFocus()}>
<uui-icon name="search" slot="prepend" id="search_icon"></uui-icon>
</uui-input>`;
@@ -136,15 +134,14 @@ export class UmbIconPickerModalElement extends UmbModalBaseElement<UmbIconPicker
(icon) => icon.name,
(icon) => html`
<uui-button
label="${icon.name}"
title="${icon.name}"
class="${icon.name === this._currentIcon ? 'selected' : ''}"
label=${icon.name}
title=${icon.name}
class=${icon.name === this._currentIcon ? 'selected' : ''}
@click=${(e: InputEvent) => this.#changeIcon(e, icon.name)}
@keyup=${(e: KeyboardEvent) => this.#changeIcon(e, icon.name)}>
<uui-icon
style="--uui-icon-color: var(${extractUmbColorVariable(this._currentColor)})"
name="${icon.name}">
</uui-icon>
name=${icon.name}></uui-icon>
</uui-button>
`,
)

View File

@@ -13,12 +13,6 @@ const modals: Array<ManifestModal> = [
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',

View File

@@ -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';

View File

@@ -1,5 +1,6 @@
import { umbExtensionsRegistry, type ManifestPropertyEditorUi } from '../../extension-registry/index.js';
import { UmbPropertyContext } from './property.context.js';
import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/extension-registry';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, html, customElement, property, state, ifDefined, nothing } from '@umbraco-cms/backoffice/external/lit';
import { createExtensionElement } from '@umbraco-cms/backoffice/extension-api';

View File

@@ -1,8 +1,8 @@
import { html, customElement, state, ifDefined, map } from '@umbraco-cms/backoffice/external/lit';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, customElement, html, ifDefined, map, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UMB_SECTION_CONTEXT } from '@umbraco-cms/backoffice/section';
import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
import type { UmbMenuStructureWorkspaceContext, UmbStructureItemModel } from '@umbraco-cms/backoffice/menu';
@customElement('umb-workspace-breadcrumb')
@@ -84,7 +84,14 @@ export class UmbWorkspaceBreadcrumbElement extends UmbLitElement {
`;
}
static styles = [UmbTextStyles];
static styles = [
UmbTextStyles,
css`
:host {
margin-left: var(--uui-size-layout-1);
}
`,
];
}
export default UmbWorkspaceBreadcrumbElement;

View File

@@ -1,12 +1,12 @@
import { html, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, customElement, html, ifDefined, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { UmbVariantDatasetWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
import { UMB_VARIANT_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
import type { UmbAppLanguageContext } from '@umbraco-cms/backoffice/language';
import { UMB_APP_LANGUAGE_CONTEXT } from '@umbraco-cms/backoffice/language';
import { UMB_SECTION_CONTEXT } from '@umbraco-cms/backoffice/section';
import { UMB_VARIANT_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
import type { UmbAppLanguageContext } from '@umbraco-cms/backoffice/language';
import type { UmbVariantDatasetWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
import type { UmbVariantStructureItemModel } from '@umbraco-cms/backoffice/menu';
@customElement('umb-workspace-variant-menu-breadcrumb')
@@ -124,7 +124,14 @@ export class UmbWorkspaceVariantMenuBreadcrumbElement extends UmbLitElement {
`;
}
static styles = [UmbTextStyles];
static styles = [
UmbTextStyles,
css`
:host {
margin-left: var(--uui-size-layout-1);
}
`,
];
}
export default UmbWorkspaceVariantMenuBreadcrumbElement;

View File

@@ -1,11 +1,10 @@
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, html, nothing, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit';
import type { UmbRoute, UmbRouterSlotInitEvent, UmbRouterSlotChangeEvent } from '@umbraco-cms/backoffice/router';
import type { ManifestWorkspaceView } from '@umbraco-cms/backoffice/extension-registry';
import { css, customElement, html, nothing, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit';
import { createExtensionElement, UmbExtensionsManifestInitializer } from '@umbraco-cms/backoffice/extension-api';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { UmbExtensionsManifestInitializer, createExtensionElement } from '@umbraco-cms/backoffice/extension-api';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import type { ManifestWorkspaceView } from '@umbraco-cms/backoffice/extension-registry';
import type { UmbRoute, UmbRouterSlotInitEvent, UmbRouterSlotChangeEvent } from '@umbraco-cms/backoffice/router';
/**
* @element umb-workspace-editor
@@ -92,14 +91,15 @@ export class UmbWorkspaceEditorElement extends UmbLitElement {
<slot name="action-menu" slot="action-menu"></slot>
${this.#renderRoutes()}
<slot></slot>
${this.enforceNoFooter
? ''
: html`
<umb-workspace-footer slot="footer">
<slot name="footer-info"></slot>
<slot name="actions" slot="actions"></slot>
</umb-workspace-footer>
`}
${when(
!this.enforceNoFooter,
() => html`
<umb-workspace-footer slot="footer">
<slot name="footer-info"></slot>
<slot name="actions" slot="actions"></slot>
</umb-workspace-footer>
`,
)}
</umb-body-layout>
`;
}
@@ -114,10 +114,10 @@ export class UmbWorkspaceEditorElement extends UmbLitElement {
(view) => view.alias,
(view) => html`
<uui-tab
.label="${view.meta.label ? this.localize.string(view.meta.label) : view.name}"
href="${this._routerPath}/view/${view.meta.pathname}"
?active="${'view/' + view.meta.pathname === this._activePath}">
<umb-icon slot="icon" name="${view.meta.icon}"></umb-icon>
.label="${view.meta.label ? this.localize.string(view.meta.label) : view.name}"
?active=${'view/' + view.meta.pathname === this._activePath}>
<umb-icon slot="icon" name=${view.meta.icon}></umb-icon>
${view.meta.label ? this.localize.string(view.meta.label) : view.name}
</uui-tab>
`,
@@ -132,8 +132,8 @@ export class UmbWorkspaceEditorElement extends UmbLitElement {
if (!this.backPath) return nothing;
return html`
<uui-button
class="back-button"
slot="header"
class="back-button"
compact
href=${this.backPath}
label=${this.localize.term('general_back')}>
@@ -143,20 +143,17 @@ export class UmbWorkspaceEditorElement extends UmbLitElement {
}
#renderRoutes() {
if (!this._routes || this._routes.length === 0) return nothing;
return html`
${this._routes && this._routes.length > 0
? html`
<umb-router-slot
id="router-slot"
.routes="${this._routes}"
@init=${(event: UmbRouterSlotInitEvent) => {
this._routerPath = event.target.absoluteRouterPath;
}}
@change=${(event: UmbRouterSlotChangeEvent) => {
this._activePath = event.target.localActiveViewPath;
}}></umb-router-slot>
`
: nothing}
<umb-router-slot
id="router-slot"
.routes=${this._routes}
@init=${(event: UmbRouterSlotInitEvent) => {
this._routerPath = event.target.absoluteRouterPath;
}}
@change=${(event: UmbRouterSlotChangeEvent) => {
this._activePath = event.target.localActiveViewPath;
}}></umb-router-slot>
`;
}

View File

@@ -84,7 +84,7 @@ export class UmbPropertyEditorUIPickerModalElement extends UmbModalBaseElement<
render() {
return html`
<umb-body-layout headline="Select Property Editor UI">
<umb-body-layout headline=${this.localize.term('propertyEditorPicker_openPropertyEditorPicker')}>
<uui-box> ${this._renderFilter()} ${this._renderGrid()} </uui-box>
<div slot="actions">
<uui-button label="Close" @click=${this._rejectModal}></uui-button>

View File

@@ -38,11 +38,8 @@ export class UmbDictionaryWorkspaceEditorElement extends UmbLitElement {
render() {
return html`
<umb-workspace-editor alias="Umb.Workspace.Dictionary">
<umb-workspace-editor alias="Umb.Workspace.Dictionary" back-path="section/dictionary/dashboard">
<div id="header" slot="header">
<uui-button href="section/dictionary/dashboard" label=${this.localize.term('general_backToOverview')} compact>
<uui-icon name="icon-arrow-left"></uui-icon>
</uui-button>
<uui-input
placeholder=${this.localize.term('placeholders_entername')}
.value=${this._name ?? ''}

View File

@@ -430,20 +430,6 @@ export class UmbDocumentBlueprintWorkspaceContext
}
}
/*
concept notes:
public saveAndPreview() {
}
*/
/*public createPropertyDatasetContext(host: UmbControllerHost, variantId: UmbVariantId) {
// TODO: [LK] Temporary workaround/hack to get the workspace to load.
const docCxt = new UmbDocumentWorkspaceContext(host);
return new UmbDocumentPropertyDataContext(host, docCxt, variantId);
}*/
public createPropertyDatasetContext(host: UmbControllerHost, variantId: UmbVariantId) {
return new UmbDocumentBlueprintPropertyDataContext(host, this, variantId);
}

View File

@@ -83,10 +83,8 @@ export class UmbDocumentTypeWorkspaceViewStructureElement extends UmbLitElement
<umb-input-document-type
.documentTypesOnly=${true}
.selection=${this._allowedContentTypeUniques ?? []}
@change="${(e: CustomEvent) => {
const sortedContentTypesList: Array<UmbContentTypeSortModel> = (
e.target as UmbInputDocumentTypeElement
).selection.map((id, index) => ({
@change="${(e: CustomEvent & { target: UmbInputDocumentTypeElement }) => {
const sortedContentTypesList: Array<UmbContentTypeSortModel> = e.target.selection.map((id, index) => ({
contentType: { unique: id },
sortOrder: index,
}));

View File

@@ -0,0 +1,25 @@
import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type {
UmbConditionConfigBase,
UmbConditionControllerArguments,
UmbExtensionCondition,
} from '@umbraco-cms/backoffice/extension-api';
import { UMB_CURRENT_USER_CONTEXT } from '@umbraco-cms/backoffice/current-user';
export class UmbAllowDocumentRecycleBinCurrentUserCondition
extends UmbConditionBase<UmbConditionConfigBase>
implements UmbExtensionCondition
{
constructor(host: UmbControllerHost, args: UmbConditionControllerArguments<UmbConditionConfigBase>) {
super(host, args);
this.consumeContext(UMB_CURRENT_USER_CONTEXT, (context) => {
this.observe(context.hasDocumentRootAccess, (hasAccess) => {
this.permitted = hasAccess === true;
});
});
}
}
export { UmbAllowDocumentRecycleBinCurrentUserCondition as api };

View File

@@ -5,6 +5,12 @@ import { manifests as treeManifests } from './tree/manifests.js';
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
export const manifests: Array<ManifestTypes> = [
{
type: 'condition',
name: 'Allow Document Recycle Bin Current User Condition',
alias: 'Umb.Condition.CurrentUser.AllowDocumentRecycleBin',
api: () => import('./allow-document-recycle-bin.condition.js'),
},
...entityActionManifests,
...menuItemManifests,
...repositoryManifests,

View File

@@ -14,6 +14,11 @@ const menuItem: ManifestMenuItemTreeKind = {
icon: 'icon-trash',
menus: [UMB_CONTENT_MENU_ALIAS],
},
conditions: [
{
alias: 'Umb.Condition.CurrentUser.AllowDocumentRecycleBin',
},
],
};
export const manifests: Array<ManifestTypes> = [menuItem];

View File

@@ -45,11 +45,10 @@ export class UmbLanguageWorkspaceEditorElement extends UmbLitElement {
}
render() {
return html`<umb-workspace-editor alias="Umb.Workspace.Language">
return html`<umb-workspace-editor
alias="Umb.Workspace.Language"
back-path="section/settings/workspace/language-root">
<div id="header" slot="header">
<uui-button label="Navigate back" href="section/settings/workspace/language-root" compact>
<uui-icon name="icon-arrow-left"></uui-icon>
</uui-button>
${this._isNew
? html`<strong>Add language</strong>`
: html`<uui-input
@@ -66,7 +65,6 @@ export class UmbLanguageWorkspaceEditorElement extends UmbLitElement {
css`
#header {
display: flex;
padding: 0 var(--uui-size-space-6);
gap: var(--uui-size-space-4);
width: 100%;
}

View File

@@ -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<monaco.editor.IActionDescriptor> = [];
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()}>
<uui-icon name="icon-width"></uui-icon>
</uui-button>
<uui-button
compact
look="secondary"
label="Link"
title="Link, &lt;Ctrl+K&gt;"
@click=${() => this.#editor?.monacoEditor?.getAction('link')?.run()}>
<uui-icon name="icon-link"></uui-icon>
</uui-button>
<uui-button
compact
look="secondary"
@@ -504,6 +480,18 @@ export class UmbInputMarkdownElement extends UUIFormControlMixin(UmbLitElement,
@click=${() => this.#editor?.monacoEditor?.getAction('image')?.run()}>
<uui-icon name="icon-picture"></uui-icon>
</uui-button>
${this._actionExtensions.map(
(action) => html`
<uui-button
compact
look="secondary"
label=${action.label}
@click=${(event: any) => this.#onActionClick(event, action)}>
<uui-icon name="icon-link"></uui-icon>
</uui-button>
`,
)}
</div>
<div>
<uui-button
@@ -544,20 +532,23 @@ export class UmbInputMarkdownElement extends UUIFormControlMixin(UmbLitElement,
}
render() {
return html` <div id="actions">${this._renderBasicActions()}</div>
return html`
<div id="actions">${this._renderBasicActions()}</div>
<umb-code-editor
language="markdown"
theme="umb-light"
.code=${this.value as string}
@keypress=${this.onKeyPress}
@input=${this.#onInput}
theme="umb-light"></umb-code-editor>
${when(this.preview && this.value, () => this.renderPreview(this.value as string))}`;
@input=${this.#onInput}>
</umb-code-editor>
${when(this.preview && this.value, () => this.renderPreview(this.value as string))}
`;
}
renderPreview(markdown: string) {
const markdownAsHtml = marked.parse(markdown) as string;
const sanitizedHtml = markdownAsHtml ? DOMPurify.sanitize(markdownAsHtml) : '';
return html`<uui-scroll-container id="preview"> ${unsafeHTML(sanitizedHtml)} </uui-scroll-container>`;
return html`<uui-scroll-container id="preview">${unsafeHTML(sanitizedHtml)}</uui-scroll-container>`;
}
static styles = [

View File

@@ -27,17 +27,19 @@ export class UmbPropertyEditorUIMarkdownEditorElement extends UmbLitElement impl
this._overlaySize = config?.getValueByAlias('overlaySize') ?? undefined;
}
#onChange(e: Event) {
this.value = (e.target as UmbInputMarkdownElement).value as string;
#onChange(event: Event & { target: UmbInputMarkdownElement }) {
this.value = event.target.value as string;
this.dispatchEvent(new UmbPropertyValueChangeEvent());
}
render() {
return html`<umb-input-markdown
?preview=${this._preview}
.overlaySize=${this._overlaySize}
@change=${this.#onChange}
.value=${this.value}></umb-input-markdown>`;
return html`
<umb-input-markdown
value=${this.value}
.overlaySize=${this._overlaySize}
?preview=${this._preview}
@change=${this.#onChange}></umb-input-markdown>
`;
}
}

View File

@@ -0,0 +1,25 @@
import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type {
UmbConditionConfigBase,
UmbConditionControllerArguments,
UmbExtensionCondition,
} from '@umbraco-cms/backoffice/extension-api';
import { UMB_CURRENT_USER_CONTEXT } from '@umbraco-cms/backoffice/current-user';
export class UmbAllowMediaRecycleBinCurrentUserCondition
extends UmbConditionBase<UmbConditionConfigBase>
implements UmbExtensionCondition
{
constructor(host: UmbControllerHost, args: UmbConditionControllerArguments<UmbConditionConfigBase>) {
super(host, args);
this.consumeContext(UMB_CURRENT_USER_CONTEXT, (context) => {
this.observe(context.hasMediaRootAccess, (hasAccess) => {
this.permitted = hasAccess === true;
});
});
}
}
export { UmbAllowMediaRecycleBinCurrentUserCondition as api };

View File

@@ -5,6 +5,12 @@ import { manifests as treeManifests } from './tree/manifests.js';
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
export const manifests: Array<ManifestTypes> = [
{
type: 'condition',
name: 'Allow Media Recycle Bin Current User Condition',
alias: 'Umb.Condition.CurrentUser.AllowMediaRecycleBin',
api: () => import('./allow-media-recycle-bin.condition.js'),
},
...entityActionManifests,
...menuItemManifests,
...repositoryManifests,

View File

@@ -14,6 +14,11 @@ const menuItem: ManifestMenuItemTreeKind = {
icon: 'icon-trash',
menus: [UMB_MEDIA_MENU_ALIAS],
},
conditions: [
{
alias: 'Umb.Condition.CurrentUser.AllowMediaRecycleBin',
},
],
};
export const manifests: Array<ManifestTypes> = [menuItem];

View File

@@ -0,0 +1 @@
export const UMB_MULTI_URL_PICKER_MODAL_ALIAS = 'Umb.Modal.MultiUrlLinkPicker';

View File

@@ -1,13 +1,12 @@
import type { UmbLinkPickerLink, UmbLinkPickerLinkType } from './types.js';
import type {
UmbLinkPickerConfig,
UmbLinkPickerModalData,
UmbLinkPickerModalValue,
} from './link-picker-modal.token.js';
import type { UmbTreeElement, UmbTreeSelectionConfiguration } from '@umbraco-cms/backoffice/tree';
import { css, html, nothing, customElement, query, state, styleMap } from '@umbraco-cms/backoffice/external/lit';
import type { UUIBooleanInputEvent, UUIInputElement } from '@umbraco-cms/backoffice/external/uui';
import type {
UmbLinkPickerConfig,
UmbLinkPickerLink,
UmbLinkPickerLinkType,
UmbLinkPickerModalData,
UmbLinkPickerModalValue,
} from '@umbraco-cms/backoffice/modal';
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
import { UMB_DOCUMENT_TREE_ALIAS } from '@umbraco-cms/backoffice/document';

View File

@@ -1,4 +1,4 @@
import '../../../components/body-layout/body-layout.element.js';
import '../../../core/components/body-layout/body-layout.element.js';
import './link-picker-modal.element.js';
import type { Meta, Story } from '@storybook/web-components';

View File

@@ -1,4 +1,6 @@
import { UmbModalToken } from './modal-token.js';
import { UMB_MULTI_URL_PICKER_MODAL_ALIAS } from './constants.js';
import type { UmbLinkPickerLink } from './types.js';
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
export interface UmbLinkPickerModalData {
config: UmbLinkPickerConfig;
@@ -7,20 +9,6 @@ export interface UmbLinkPickerModalData {
export type UmbLinkPickerModalValue = { link: UmbLinkPickerLink };
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;
}
export type UmbLinkPickerLinkType = 'document' | 'external' | 'media';
// TODO: investigate: this looks more like a property editor configuration. Is this used in the correct way?
export interface UmbLinkPickerConfig {
hideAnchor?: boolean;
@@ -28,7 +16,7 @@ export interface UmbLinkPickerConfig {
}
export const UMB_LINK_PICKER_MODAL = new UmbModalToken<UmbLinkPickerModalData, UmbLinkPickerModalValue>(
'Umb.Modal.LinkPicker',
UMB_MULTI_URL_PICKER_MODAL_ALIAS,
{
modal: {
type: 'sidebar',

View File

@@ -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'),
},
];

View File

@@ -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;
}

View File

@@ -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<ManifestTypes> = [
...modalManifests,
...monacoMarkdownEditorManifests,
...propertyEditorManifests,
...tinyMceManifests,
];

View File

@@ -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'),
},
];

View File

@@ -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 };

View File

@@ -0,0 +1 @@
export * from './multi-url-picker.element.js';

View File

@@ -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<UmbLinkPickerLink>(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;
}
}

View File

@@ -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<UmbMultiUrlPickerElement> = {
title: 'Components/Inputs/Multi URL',
component: 'umb-input-multi-url',
};
export default meta;
type Story = StoryObj<UmbMultiUrlPickerElement>;
export const Overview: Story = {
args: {},
};

View File

@@ -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,
];

View File

@@ -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`
<umb-input-multi-url
<umb-multi-url-picker
.alias=${this._alias}
.ignoreUserStartNodes=${this._ignoreUserStartNodes}
.max=${this._maxNumber}
@@ -73,7 +76,7 @@ export class UmbPropertyEditorUIMultiUrlPickerElement extends UmbLitElement impl
.variantId=${this._variantId}
?hide-anchor=${this._hideAnchor}
@change=${this.#onChange}>
</umb-input-multi-url>
</umb-multi-url-picker>
`;
}
}

View File

@@ -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',
},
],
},
},
];

View File

@@ -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;
}

View File

@@ -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'),
},
];

View File

@@ -125,10 +125,8 @@ export class UmbWorkspacePackageBuilderElement extends UmbLitElement {
render() {
if (!this.workspaceAlias) return nothing;
return html`
<umb-workspace-editor alias=${this.workspaceAlias}>
${this.#renderHeader()}
${this.#renderEditors()}
${this.#renderActions()}
<umb-workspace-editor alias=${this.workspaceAlias} back-path="section/packages/view/created">
${this.#renderHeader()} ${this.#renderEditors()} ${this.#renderActions()}
</umb-workspace-editor>
`;
}
@@ -137,9 +135,6 @@ export class UmbWorkspacePackageBuilderElement extends UmbLitElement {
if (!this._package) return nothing;
return html`
<div id="header" slot="header">
<uui-button href="section/packages/view/created" label=${this.localize.term('general_backToOverview')} compact>
<uui-icon name="icon-arrow-left"></uui-icon>
</uui-button>
<uui-input
id="package-name-input"
required
@@ -178,17 +173,10 @@ export class UmbWorkspacePackageBuilderElement extends UmbLitElement {
#renderEditors() {
return html`
<uui-box headline="Package Content">
${this.#renderDocumentSection()}
${this.#renderMediaSection()}
${this.#renderDocumentTypeSection()}
${this.#renderMediaTypeSection()}
${this.#renderLanguageSection()}
${this.#renderDictionarySection()}
${this.#renderDataTypeSection()}
${this.#renderTemplateSection()}
${this.#renderStylesheetsSection()}
${this.#renderScriptsSection()}
${this.#renderPartialViewSection()}
${this.#renderDocumentSection()} ${this.#renderMediaSection()} ${this.#renderDocumentTypeSection()}
${this.#renderMediaTypeSection()} ${this.#renderLanguageSection()} ${this.#renderDictionarySection()}
${this.#renderDataTypeSection()} ${this.#renderTemplateSection()} ${this.#renderStylesheetsSection()}
${this.#renderScriptsSection()} ${this.#renderPartialViewSection()}
</uui-box>
`;
}

View File

@@ -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;
}
}

View File

@@ -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<ManifestPropertyEditorUi> = [
export const manifests: Array<ManifestTypes> = [
propertyEditorUiManifest,
bulkActionPermissions,
columnConfiguration,
layoutConfiguration,
orderBy,
];
export const manifests: Array<ManifestTypes> = [
propertyEditorUiManifest,
legacyPropertyEditorUiManifest,
...config,
schema,
];

View File

@@ -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<ManifestTypes> = [
...iconPickerManifests,
...labelManifests,
...multipleTextStringManifests,
...multiUrlPickerManifests,
...numberManifests,
...radioButtonListManifests,
...sliderManifests,

View File

@@ -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<ManifestTypes> = [manifest, schemaManifest];

View File

@@ -36,7 +36,9 @@ export class UmbRelationTypeWorkspaceEditorElement extends UmbLitElement {
render() {
return html`
<umb-workspace-editor alias="Umb.Workspace.RelationType">
<umb-workspace-editor
alias="Umb.Workspace.RelationType"
back-path="section/settings/workspace/relation-type-root">
<div id="header" slot="header">
<uui-input id="name" .value=${this._name} readonly>
<div id="alias" slot="append">${this._alias}</div>

View File

@@ -16,26 +16,6 @@ export const manifests: Array<ManifestTinyMcePlugin> = [
],
},
},
{
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',

View File

@@ -14,6 +14,8 @@ export class UmbCurrentUserContext extends UmbContextBase<UmbCurrentUserContext>
readonly unique = this.#currentUser.asObservablePart((user) => user?.unique);
readonly languageIsoCode = this.#currentUser.asObservablePart((user) => user?.languageIsoCode);
readonly hasDocumentRootAccess = this.#currentUser.asObservablePart((user) => user?.hasDocumentRootAccess);
readonly hasMediaRootAccess = this.#currentUser.asObservablePart((user) => user?.hasMediaRootAccess);
#authContext?: typeof UMB_AUTH_CONTEXT.TYPE;
#currentUserRepository = new UmbCurrentUserRepository(this);

View File

@@ -30,20 +30,22 @@ export class UmbCurrentUserServerDataSource {
if (data) {
const user: UmbCurrentUserModel = {
unique: data.id,
email: data.email,
userName: data.userName,
name: data.name,
languageIsoCode: data.languageIsoCode || 'en-us', // TODO: make global variable
documentStartNodeUniques: data.documentStartNodeIds,
mediaStartNodeUniques: data.mediaStartNodeIds,
avatarUrls: data.avatarUrls,
languages: data.languages,
hasAccessToAllLanguages: data.hasAccessToAllLanguages,
fallbackPermissions: data.fallbackPermissions,
permissions: data.permissions,
allowedSections: data.allowedSections,
avatarUrls: data.avatarUrls,
documentStartNodeUniques: data.documentStartNodeIds,
email: data.email,
fallbackPermissions: data.fallbackPermissions,
hasAccessToAllLanguages: data.hasAccessToAllLanguages,
hasDocumentRootAccess: data.hasDocumentRootAccess,
hasMediaRootAccess: data.hasMediaRootAccess,
isAdmin: data.isAdmin,
languageIsoCode: data.languageIsoCode || 'en-us', // TODO: make global variable
languages: data.languages,
mediaStartNodeUniques: data.mediaStartNodeIds,
name: data.name,
permissions: data.permissions,
unique: data.id,
userName: data.userName,
};
return { data: user };
}

View File

@@ -53,11 +53,7 @@ export class UmbCurrentUserThemeUserProfileAppElement extends UmbLitElement {
<b>Select Theme</b>
<p>Experimental. Only light theme is fully supported</p>
</div>
<uui-select
label="Select theme"
.options=${this._themes}
.value=${this._themeAlias}
@change=${this.#onThemeChange}></uui-select>
<uui-select label="Select theme" .options=${this._themes} @change=${this.#onThemeChange}></uui-select>
</uui-box>
`;
}

View File

@@ -7,20 +7,22 @@ import type {
} from '@umbraco-cms/backoffice/external/backend-api';
export interface UmbCurrentUserModel {
unique: string;
email: string;
userName: string;
name: string;
languageIsoCode: string;
documentStartNodeUniques: Array<string>;
mediaStartNodeUniques: Array<string>;
avatarUrls: Array<string>;
languages: Array<string>;
hasAccessToAllLanguages: boolean;
allowedSections: Array<string>;
avatarUrls: Array<string>;
documentStartNodeUniques: Array<string>;
email: string;
fallbackPermissions: Array<string>;
permissions: Array<DocumentPermissionPresentationModel | UnknownTypePermissionPresentationModel>;
hasAccessToAllLanguages: boolean;
hasDocumentRootAccess: boolean;
hasMediaRootAccess: boolean;
isAdmin: boolean;
languageIsoCode: string;
languages: Array<string>;
mediaStartNodeUniques: Array<string>;
name: string;
permissions: Array<DocumentPermissionPresentationModel | UnknownTypePermissionPresentationModel>;
unique: string;
userName: string;
}
export type UmbCurrentUserMfaProviderModel = UserTwoFactorProviderModel;

View File

@@ -135,8 +135,8 @@ export class UmbUserInputElement extends UUIFormControlMixin(UmbLitElement, '')
<uui-button
id="btn-add"
look="placeholder"
@click=${this.#openPicker}
label="${this.localize.term('general_choose')}"></uui-button>
label=${this.localize.term('general_choose')}
@click=${this.#openPicker}></uui-button>
`;
}