Merge branch 'main' into v14/feature/save-publish-language-permissions

This commit is contained in:
Mads Rasmussen
2024-09-09 08:17:15 +02:00
committed by GitHub
18 changed files with 263 additions and 93 deletions

View File

@@ -59,7 +59,7 @@
},
"devDependencies": {
"@babel/core": "^7.24.9",
"@eslint/js": "^9.7.0",
"@eslint/js": "^9.9.1",
"@hey-api/openapi-ts": "^0.48.3",
"@mdx-js/react": "^3.0.1",
"@open-wc/testing": "^4.0.0",
@@ -91,7 +91,7 @@
"eslint-plugin-lit": "^1.14.0",
"eslint-plugin-local-rules": "^3.0.2",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-wc": "^2.1.0",
"eslint-plugin-wc": "^2.1.1",
"glob": "^11.0.0",
"globals": "^15.8.0",
"lucide-static": "^0.424.0",
@@ -2616,9 +2616,9 @@
}
},
"node_modules/@eslint/js": {
"version": "9.9.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.0.tgz",
"integrity": "sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==",
"version": "9.9.1",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz",
"integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==",
"dev": true,
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -12770,9 +12770,9 @@
}
},
"node_modules/eslint-plugin-wc": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-2.1.0.tgz",
"integrity": "sha512-s/BGOtmpgQ2yifR6EC1OM9t0DwYLgg4ZAL07Kw4eXvBb5TYaPafI+65tswvnZvhH8FqcjERLbBZPPvYsvinkfg==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-2.1.1.tgz",
"integrity": "sha512-GfJo05ZgWfwAFbW6Gkf+9CMOIU6fmbd3b4nm+PKESHgUdUTmi7vawlELCrzOhdiQjXUPZxDfFIVxYt9D/v/GdQ==",
"dev": true,
"dependencies": {
"is-valid-element-name": "^1.0.0",
@@ -12810,6 +12810,15 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/@eslint/js": {
"version": "9.9.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.0.tgz",
"integrity": "sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==",
"dev": true,
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/eslint/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",

View File

@@ -231,7 +231,7 @@
},
"devDependencies": {
"@babel/core": "^7.24.9",
"@eslint/js": "^9.7.0",
"@eslint/js": "^9.9.1",
"@hey-api/openapi-ts": "^0.48.3",
"@mdx-js/react": "^3.0.1",
"@open-wc/testing": "^4.0.0",
@@ -263,7 +263,7 @@
"eslint-plugin-lit": "^1.14.0",
"eslint-plugin-local-rules": "^3.0.2",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-wc": "^2.1.0",
"eslint-plugin-wc": "^2.1.1",
"glob": "^11.0.0",
"globals": "^15.8.0",
"lucide-static": "^0.424.0",

View File

@@ -197,15 +197,17 @@ export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefi
}
#renderAddButton() {
if (this.selection.length >= this.max) return;
return html`
<uui-button
if (this.selection.length >= this.max) return nothing;
if (this.readonly && this.selection.length > 0) {
return nothing;
} else {
return html` <uui-button
id="btn-add"
look="placeholder"
@click=${this.#openPicker}
label=${this.localize.term('general_choose')}
?disabled=${this.readonly}></uui-button>
`;
?disabled=${this.readonly}></uui-button>`;
}
}
#renderItems() {

View File

@@ -1,4 +1,13 @@
import { css, customElement, html, property, query, state, unsafeHTML } from '@umbraco-cms/backoffice/external/lit';
import {
css,
customElement,
html,
nothing,
property,
query,
state,
unsafeHTML,
} from '@umbraco-cms/backoffice/external/lit';
import { createExtensionApi } from '@umbraco-cms/backoffice/extension-api';
import { marked } from '@umbraco-cms/backoffice/external/marked';
import { monaco } from '@umbraco-cms/backoffice/external/monaco-editor';
@@ -34,6 +43,22 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin(UmbLitElement,
@property()
overlaySize?: UUIModalSidebarSize;
/**
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
* @type {boolean}
* @attr
* @default false
*/
@property({ type: Boolean, reflect: true })
public get readonly() {
return this.#readonly;
}
public set readonly(value) {
this.#readonly = value;
this.#editor?.monacoEditor?.updateOptions({ readOnly: this.#readonly });
}
#readonly = false;
#editor?: UmbCodeEditorController;
@query('umb-code-editor')
@@ -50,6 +75,9 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin(UmbLitElement,
try {
this.#editor = this._codeEditor?.editor;
// Set read only mode
this.#editor?.monacoEditor?.updateOptions({ readOnly: this.#readonly });
// TODO: make all action into extensions
this.observe(umbExtensionsRegistry.byType('monacoMarkdownEditorAction'), (manifests) => {
manifests.forEach(async (manifest) => {
@@ -416,6 +444,7 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin(UmbLitElement,
}
#renderToolbar() {
if (this.readonly) return nothing;
return html`
<div id="toolbar">
<uui-button-group>

View File

@@ -11,6 +11,7 @@ const manifest: ManifestPropertyEditorUi = {
propertyEditorSchemaAlias: 'Umbraco.MarkdownEditor',
icon: 'icon-code',
group: 'richContent',
supportsReadOnly: true,
settings: {
properties: [
{

View File

@@ -17,6 +17,15 @@ export class UmbPropertyEditorUIMarkdownEditorElement extends UmbLitElement impl
@property()
value = '';
/**
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
* @type {boolean}
* @attr
* @default false
*/
@property({ type: Boolean, reflect: true })
readonly = false;
@state()
private _preview?: boolean;
@@ -41,7 +50,8 @@ export class UmbPropertyEditorUIMarkdownEditorElement extends UmbLitElement impl
value=${this.value}
.overlaySize=${this._overlaySize}
?preview=${this._preview}
@change=${this.#onChange}></umb-input-markdown>
@change=${this.#onChange}
?readonly=${this.readonly}></umb-input-markdown>
`;
}
}

View File

@@ -1,7 +1,16 @@
import type { UmbMediaCardItemModel } from '../../modals/index.js';
import type { UmbMediaItemModel } from '../../repository/index.js';
import { UmbMediaPickerInputContext } from './input-media.context.js';
import { css, customElement, html, ifDefined, property, repeat, state } from '@umbraco-cms/backoffice/external/lit';
import {
css,
customElement,
html,
ifDefined,
nothing,
property,
repeat,
state,
} from '@umbraco-cms/backoffice/external/lit';
import { splitStringToArray } from '@umbraco-cms/backoffice/utils';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
@@ -13,7 +22,6 @@ import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
import '@umbraco-cms/backoffice/imaging';
const elementName = 'umb-input-media';
@customElement(elementName)
export class UmbInputMediaElement extends UmbFormControlMixin<string | undefined, typeof UmbLitElement>(UmbLitElement) {
#sorter = new UmbSorterController<string>(this, {
@@ -116,6 +124,27 @@ export class UmbInputMediaElement extends UmbFormControlMixin<string | undefined
return this.selection.length > 0 ? this.selection.join(',') : undefined;
}
/**
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
* @type {boolean}
* @attr
* @default
*/
@property({ type: Boolean, reflect: true })
public get readonly() {
return this.#readonly;
}
public set readonly(value) {
this.#readonly = value;
if (this.#readonly) {
this.#sorter.disable();
} else {
this.#sorter.enable();
}
}
#readonly = false;
@state()
private _editMediaPath = '';
@@ -182,7 +211,7 @@ export class UmbInputMediaElement extends UmbFormControlMixin<string | undefined
}
#renderItems() {
if (!this._cards?.length) return;
if (!this._cards?.length) return nothing;
return html`
${repeat(
this._cards,
@@ -193,44 +222,54 @@ export class UmbInputMediaElement extends UmbFormControlMixin<string | undefined
}
#renderAddButton() {
if (this._cards && this.max && this._cards.length >= this.max) return;
return html`
<uui-button
id="btn-add"
look="placeholder"
@click=${this.#openPicker}
label=${this.localize.term('general_choose')}>
<uui-icon name="icon-add"></uui-icon>
${this.localize.term('general_choose')}
</uui-button>
`;
// TODO: Stop preventing adding more, instead implement proper validation for user feedback. [NL]
if (this._cards && this.max && this._cards.length >= this.max) return nothing;
if (this.readonly && this._cards.length > 0) {
return nothing;
} else {
return html`
<uui-button
id="btn-add"
look="placeholder"
@click=${this.#openPicker}
label=${this.localize.term('general_choose')}
?disabled=${this.readonly}>
<uui-icon name="icon-add"></uui-icon>
${this.localize.term('general_choose')}
</uui-button>
`;
}
}
#renderItem(item: UmbMediaCardItemModel) {
const href = this.readonly ? undefined : `${this._editMediaPath}edit/${item.unique}`;
return html`
<uui-card-media
name=${ifDefined(item.name === null ? undefined : item.name)}
detail=${ifDefined(item.unique)}
href="${this._editMediaPath}edit/${item.unique}">
href="${ifDefined(href)}"
?readonly=${this.readonly}>
<umb-imaging-thumbnail
unique=${item.unique}
alt=${item.name}
icon=${item.mediaType.icon}></umb-imaging-thumbnail>
${this.#renderIsTrashed(item)}
<uui-action-bar slot="actions">
<uui-button
label=${this.localize.term('general_remove')}
look="secondary"
@click=${() => this.#onRemove(item)}>
<uui-icon name="icon-trash"></uui-icon>
</uui-button>
</uui-action-bar>
<uui-action-bar slot="actions"> ${this.#renderRemoveAction(item)}</uui-action-bar>
</uui-card-media>
`;
}
#renderRemoveAction(item: UmbMediaCardItemModel) {
if (this.readonly) return nothing;
return html`
<uui-button label=${this.localize.term('general_remove')} look="secondary" @click=${() => this.#onRemove(item)}>
<uui-icon name="icon-trash"></uui-icon>
</uui-button>
`;
}
#renderIsTrashed(item: UmbMediaCardItemModel) {
if (!item.isTrashed) return;
if (!item.isTrashed) return nothing;
return html`
<uui-tag size="s" slot="tag" color="danger">
<umb-localize key="mediaPicker_trashed">Trashed</umb-localize>

View File

@@ -361,18 +361,23 @@ export class UmbInputRichMediaElement extends UUIFormControlMixin(UmbLitElement,
}
#renderAddButton() {
// TODO: Stop preventing adding more, instead implement proper validation for user feedback. [NL]
if ((this._cards && this.max && this._cards.length >= this.max) || (this._cards.length && !this.multiple)) return;
return html`
<uui-button
id="btn-add"
look="placeholder"
@click=${this.#openPicker}
label=${this.localize.term('general_choose')}
?disabled=${this.readonly}>
<uui-icon name="icon-add"></uui-icon>
${this.localize.term('general_choose')}
</uui-button>
`;
if (this.readonly && this._cards.length > 0) {
return nothing;
} else {
return html`
<uui-button
id="btn-add"
look="placeholder"
@click=${this.#openPicker}
label=${this.localize.term('general_choose')}
?disabled=${this.readonly}>
<uui-icon name="icon-add"></uui-icon>
${this.localize.term('general_choose')}
</uui-button>
`;
}
}
#renderItem(item: UmbRichMediaCardModel) {

View File

@@ -9,5 +9,6 @@ export const manifest: ManifestPropertyEditorUi = {
label: 'Media Entity Picker',
icon: 'icon-picture',
group: 'pickers',
supportsReadOnly: true,
},
};

View File

@@ -19,6 +19,15 @@ export class UmbPropertyEditorUIMediaEntityPickerElement extends UmbLitElement i
this._max = minMax?.max ?? Infinity;
}
/**
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
* @type {boolean}
* @attr
* @default false
*/
@property({ type: Boolean, reflect: true })
readonly = false;
@state()
_min: number = 0;
@@ -36,6 +45,7 @@ export class UmbPropertyEditorUIMediaEntityPickerElement extends UmbLitElement i
.min=${this._min}
.max=${this._max}
.value=${this.value}
?readonly=${this.readonly}
@change=${this.#onChange}></umb-input-media>
`;
}

View File

@@ -1,6 +1,6 @@
import type { UmbMemberGroupItemModel } from '../../repository/index.js';
import { UmbMemberGroupPickerInputContext } from './input-member-group.context.js';
import { css, html, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit';
import { css, html, customElement, property, state, repeat, nothing } from '@umbraco-cms/backoffice/external/lit';
import { splitStringToArray } from '@umbraco-cms/backoffice/utils';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
@@ -100,6 +100,27 @@ export class UmbInputMemberGroupElement extends UmbFormControlMixin<string | und
@property({ type: Object, attribute: false })
public filter: (memberGroup: UmbMemberGroupItemModel) => boolean = () => true;
/**
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
* @type {boolean}
* @attr
* @default false
*/
@property({ type: Boolean, reflect: true })
public get readonly() {
return this.#readonly;
}
public set readonly(value) {
this.#readonly = value;
if (this.#readonly) {
this.#sorter.disable();
} else {
this.#sorter.enable();
}
}
#readonly = false;
@state()
private _editMemberGroupPath = '';
@@ -168,36 +189,38 @@ export class UmbInputMemberGroupElement extends UmbFormControlMixin<string | und
}
#renderAddButton() {
if (this.max === 1 && this.selection.length >= this.max) return;
return html`<uui-button
id="btn-add"
look="placeholder"
@click=${this.#openPicker}
label=${this.localize.term('general_choose')}></uui-button>`;
if (this.max === 1 && this.selection.length >= this.max) return nothing;
if (this.readonly && this.selection.length > 0) {
return nothing;
} else {
return html`<uui-button
id="btn-add"
look="placeholder"
@click=${this.#openPicker}
label=${this.localize.term('general_choose')}
?disabled=${this.readonly}></uui-button>`;
}
}
#renderItem(item: UmbMemberGroupItemModel) {
if (!item.unique) return;
if (!item.unique) return nothing;
return html`
<uui-ref-node name=${item.name} id=${item.unique}>
<uui-action-bar slot="actions">
${this.#renderOpenButton(item)}
<uui-button @click=${() => this.#removeItem(item)} label=${this.localize.term('general_remove')}></uui-button>
</uui-action-bar>
<uui-ref-node
name=${item.name}
id=${item.unique}
href="${this._editMemberGroupPath}edit/${item.unique}"
?readonly=${this.readonly}>
<uui-action-bar slot="actions"> ${this.#renderRemoveButton(item)} </uui-action-bar>
<umb-icon slot="icon" name="icon-users"></umb-icon>
</uui-ref-node>
`;
}
#renderOpenButton(item: UmbMemberGroupItemModel) {
if (!this.showOpenButton) return;
return html`
<uui-button
href="${this._editMemberGroupPath}edit/${item.unique}"
label="${this.localize.term('general_open')} ${item.name}">
${this.localize.term('general_open')}
</uui-button>
`;
#renderRemoveButton(item: UmbMemberGroupItemModel) {
if (this.readonly) return nothing;
return html`<uui-button
@click=${() => this.#removeItem(item)}
label=${this.localize.term('general_remove')}></uui-button>`;
}
static override styles = [

View File

@@ -12,6 +12,7 @@ export const manifests: Array<ManifestTypes> = [
propertyEditorSchemaAlias: 'Umbraco.MemberGroupPicker',
icon: 'icon-users-alt',
group: 'people',
supportsReadOnly: true,
},
},
memberGroupSchemaManifest,

View File

@@ -22,6 +22,15 @@ export class UmbPropertyEditorUIMemberGroupPickerElement extends UmbLitElement i
this._max = minMax?.max ?? Infinity;
}
/**
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
* @type {boolean}
* @attr
* @default false
*/
@property({ type: Boolean, reflect: true })
readonly = false;
@state()
_min = 0;
@@ -40,7 +49,8 @@ export class UmbPropertyEditorUIMemberGroupPickerElement extends UmbLitElement i
.max=${this._max}
.value=${this.value}
?showOpenButton=${true}
@change=${this.#onChange}></umb-input-member-group>
@change=${this.#onChange}
?readonly=${this.readonly}></umb-input-member-group>
`;
}
}

View File

@@ -196,14 +196,18 @@ export class UmbInputMemberElement extends UmbFormControlMixin<string | undefine
#renderAddButton() {
if (this.selection.length >= this.max) return nothing;
return html`
<uui-button
id="btn-add"
look="placeholder"
@click=${this.#openPicker}
label=${this.localize.term('general_choose')}
?disabled=${this.readonly}></uui-button>
`;
if (this.readonly && this.selection.length > 0) {
return nothing;
} else {
return html`
<uui-button
id="btn-add"
look="placeholder"
@click=${this.#openPicker}
label=${this.localize.term('general_choose')}
?disabled=${this.readonly}></uui-button>
`;
}
}
#renderItem(item: UmbMemberItemModel) {

View File

@@ -272,15 +272,19 @@ export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement,
}
#renderAddButton() {
if (this.max === 1 && this.urls && this.urls.length >= this.max) return;
return html`
<uui-button
id="btn-add"
look="placeholder"
label=${this.localize.term('general_add')}
.href=${this._modalRoute?.({ index: -1 })}
?disabled=${this.readonly}></uui-button>
`;
if (this.max === 1 && this.urls && this.urls.length >= this.max) return nothing;
if (this.readonly && this.urls.length > 0) {
return nothing;
} else {
return html`
<uui-button
id="btn-add"
look="placeholder"
label=${this.localize.term('general_add')}
.href=${this._modalRoute?.({ index: -1 })}
?disabled=${this.readonly}></uui-button>
`;
}
}
#renderItems() {

View File

@@ -8,7 +8,6 @@ import { splitStringToArray } from '@umbraco-cms/backoffice/utils';
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
const elementName = 'umb-input-content';
@customElement(elementName)
export class UmbInputContentElement extends UmbFormControlMixin<string | undefined, typeof UmbLitElement>(
UmbLitElement,
@@ -79,6 +78,15 @@ export class UmbInputContentElement extends UmbFormControlMixin<string | undefin
return this.#selection.length > 0 ? this.#selection.join(',') : undefined;
}
/**
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
* @type {boolean}
* @attr
* @default false
*/
@property({ type: Boolean, reflect: true })
readonly = false;
#entityTypeLookup = { content: 'document', media: 'media', member: 'member' };
#selection: Array<string> = [];
@@ -117,6 +125,7 @@ export class UmbInputContentElement extends UmbFormControlMixin<string | undefin
.max=${this.max}
.maxMessage=${this.maxMessage}
?showOpenButton=${this.showOpenButton}
?readonly=${this.readonly}
@change=${this.#onChange}></umb-input-document>
`;
}
@@ -131,6 +140,7 @@ export class UmbInputContentElement extends UmbFormControlMixin<string | undefin
.max=${this.max}
.maxMessage=${this.maxMessage}
?showOpenButton=${this.showOpenButton}
?readonly=${this.readonly}
@change=${this.#onChange}></umb-input-media>
`;
}
@@ -145,6 +155,7 @@ export class UmbInputContentElement extends UmbFormControlMixin<string | undefin
.max=${this.max}
.maxMessage=${this.maxMessage}
?showOpenButton=${this.showOpenButton}
?readonly=${this.readonly}
@change=${this.#onChange}></umb-input-member>
`;
}

View File

@@ -14,6 +14,7 @@ const manifest: ManifestPropertyEditorUi = {
icon: 'icon-page-add',
group: 'pickers',
propertyEditorSchemaAlias: 'Umbraco.MultiNodeTreePicker',
supportsReadOnly: true,
settings: {
properties: [
{

View File

@@ -37,6 +37,15 @@ export class UmbPropertyEditorUIContentPickerElement
}
#value?: UmbContentPickerValueType = [];
/**
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
* @type {boolean}
* @attr
* @default false
*/
@property({ type: Boolean, reflect: true })
readonly = false;
@state()
_type: UmbContentPickerSource['type'] = 'content';
@@ -150,6 +159,7 @@ export class UmbPropertyEditorUIContentPickerElement
.startNode=${startNode}
.allowedContentTypeIds=${this._allowedContentTypeUniques ?? ''}
?showOpenButton=${this._showOpenButton}
?readonly=${this.readonly}
@change=${this.#onChange}></umb-input-content>
`;
}