Merge remote-tracking branch 'origin/main' into feature/easy-way-to-produce-a-set-of-properties
This commit is contained in:
@@ -33,3 +33,4 @@ export * from './tooltip-menu/index.js';
|
||||
export * from './variant-selector/index.js';
|
||||
export * from './popover-layout/index.js';
|
||||
export * from './multiple-text-string-input/index.js';
|
||||
export * from './multiple-color-picker-input/index.js';
|
||||
|
||||
@@ -13,9 +13,9 @@ import {
|
||||
|
||||
@customElement('umb-image-cropper-focus-setter')
|
||||
export class UmbImageCropperFocusSetterElement extends LitElement {
|
||||
@query('#image') imageElement!: HTMLImageElement;
|
||||
@query('#wrapper') wrapperElement!: HTMLImageElement;
|
||||
@query('#focal-point') focalPointElement!: HTMLImageElement;
|
||||
@query('#image') imageElement?: HTMLImageElement;
|
||||
@query('#wrapper') wrapperElement?: HTMLImageElement;
|
||||
@query('#focal-point') focalPointElement?: HTMLImageElement;
|
||||
|
||||
@property({ type: String }) src?: string;
|
||||
@property({ attribute: false }) focalPoint: UmbImageCropperFocalPoint = { left: 0.5, top: 0.5 };
|
||||
@@ -35,7 +35,7 @@ export class UmbImageCropperFocusSetterElement extends LitElement {
|
||||
protected updated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
|
||||
super.updated(_changedProperties);
|
||||
|
||||
if (_changedProperties.has('focalPoint')) {
|
||||
if (_changedProperties.has('focalPoint') && this.focalPointElement) {
|
||||
this.focalPointElement.style.left = `calc(${this.focalPoint.left * 100}% - ${this.#DOT_RADIUS}px)`;
|
||||
this.focalPointElement.style.top = `calc(${this.focalPoint.top * 100}% - ${this.#DOT_RADIUS}px)`;
|
||||
}
|
||||
@@ -45,23 +45,28 @@ export class UmbImageCropperFocusSetterElement extends LitElement {
|
||||
super.firstUpdated(_changedProperties);
|
||||
|
||||
this.style.setProperty('--dot-radius', `${this.#DOT_RADIUS}px`);
|
||||
this.focalPointElement.style.left = `calc(${this.focalPoint.left * 100}% - ${this.#DOT_RADIUS}px)`;
|
||||
this.focalPointElement.style.top = `calc(${this.focalPoint.top * 100}% - ${this.#DOT_RADIUS}px)`;
|
||||
|
||||
this.imageElement.onload = () => {
|
||||
const imageAspectRatio = this.imageElement.naturalWidth / this.imageElement.naturalHeight;
|
||||
if (this.focalPointElement) {
|
||||
this.focalPointElement.style.left = `calc(${this.focalPoint.left * 100}% - ${this.#DOT_RADIUS}px)`;
|
||||
this.focalPointElement.style.top = `calc(${this.focalPoint.top * 100}% - ${this.#DOT_RADIUS}px)`;
|
||||
}
|
||||
if (this.imageElement) {
|
||||
this.imageElement.onload = () => {
|
||||
if (!this.imageElement || !this.wrapperElement) return;
|
||||
const imageAspectRatio = this.imageElement.naturalWidth / this.imageElement.naturalHeight;
|
||||
|
||||
if (imageAspectRatio > 1) {
|
||||
this.imageElement.style.width = '100%';
|
||||
this.wrapperElement.style.width = '100%';
|
||||
} else {
|
||||
this.imageElement.style.height = '100%';
|
||||
this.wrapperElement.style.height = '100%';
|
||||
}
|
||||
if (imageAspectRatio > 1) {
|
||||
this.imageElement.style.width = '100%';
|
||||
this.wrapperElement.style.width = '100%';
|
||||
} else {
|
||||
this.imageElement.style.height = '100%';
|
||||
this.wrapperElement.style.height = '100%';
|
||||
}
|
||||
|
||||
this.imageElement.style.aspectRatio = `${imageAspectRatio}`;
|
||||
this.wrapperElement.style.aspectRatio = `${imageAspectRatio}`;
|
||||
};
|
||||
this.imageElement.style.aspectRatio = `${imageAspectRatio}`;
|
||||
this.wrapperElement.style.aspectRatio = `${imageAspectRatio}`;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async #addEventListeners() {
|
||||
@@ -92,6 +97,7 @@ export class UmbImageCropperFocusSetterElement extends LitElement {
|
||||
|
||||
#onSetFocalPoint(event: MouseEvent) {
|
||||
event.preventDefault();
|
||||
if (!this.focalPointElement || !this.imageElement) return;
|
||||
|
||||
const image = this.imageElement.getBoundingClientRect();
|
||||
|
||||
|
||||
@@ -109,8 +109,8 @@ export class UmbInputImageCropperElement extends LitElement {
|
||||
.focalPoint=${this.focalPoint}
|
||||
.src=${this.src}></umb-image-cropper-focus-setter>
|
||||
<div id="actions">
|
||||
<uui-button>Remove files (NOT IMPLEMENTED YET)</uui-button>
|
||||
<uui-button @click=${this.#onResetFocalPoint}>Reset focal point</uui-button>
|
||||
<uui-button label="Remove files">Remove files (NOT IMPLEMENTED YET)</uui-button>
|
||||
<uui-button label="Reset focal point" @click=${this.#onResetFocalPoint}>Reset focal point</uui-button>
|
||||
</div> `;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './multiple-color-picker-input.element.js';
|
||||
export * from './multiple-color-picker-item-input.element.js';
|
||||
@@ -0,0 +1,254 @@
|
||||
import { UMB_DATA_TYPE_WORKSPACE_CONTEXT } from '../../data-type/workspace/data-type-workspace.context.js';
|
||||
import { UmbMultipleColorPickerItemInputElement } from './multiple-color-picker-item-input.element.js';
|
||||
import type { UmbSwatchDetails } from '@umbraco-cms/backoffice/models';
|
||||
import {
|
||||
css,
|
||||
html,
|
||||
nothing,
|
||||
repeat,
|
||||
customElement,
|
||||
property,
|
||||
state,
|
||||
ifDefined,
|
||||
} from '@umbraco-cms/backoffice/external/lit';
|
||||
import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbInputEvent, UmbChangeEvent, UmbDeleteEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter';
|
||||
|
||||
const SORTER_CONFIG: UmbSorterConfig<UmbSwatchDetails> = {
|
||||
compareElementToModel: (element: HTMLElement, model: UmbSwatchDetails) => {
|
||||
return element.getAttribute('data-sort-entry-id') === model.value;
|
||||
},
|
||||
querySelectModelToElement: (container: HTMLElement, modelEntry: UmbSwatchDetails) => {
|
||||
return container.querySelector('data-sort-entry-id=[' + modelEntry.value + ']');
|
||||
},
|
||||
identifier: 'Umb.SorterIdentifier.ColorEditor',
|
||||
itemSelector: 'umb-multiple-color-picker-item-input',
|
||||
containerSelector: '#sorter-wrapper',
|
||||
};
|
||||
|
||||
/**
|
||||
* @element umb-multiple-color-picker-input
|
||||
*/
|
||||
@customElement('umb-multiple-color-picker-input')
|
||||
export class UmbMultipleColorPickerInputElement extends FormControlMixin(UmbLitElement) {
|
||||
#prevalueSorter = new UmbSorterController(this, {
|
||||
...SORTER_CONFIG,
|
||||
|
||||
performItemInsert: (args) => {
|
||||
const frozenArray = [...this.items];
|
||||
const indexToMove = frozenArray.findIndex((x) => x.value === args.item.value);
|
||||
|
||||
frozenArray.splice(indexToMove, 1);
|
||||
frozenArray.splice(args.newIndex, 0, args.item);
|
||||
this.items = frozenArray;
|
||||
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
|
||||
return true;
|
||||
},
|
||||
performItemRemove: (args) => {
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* This is a minimum amount of selected items in this input.
|
||||
* @type {number}
|
||||
* @attr
|
||||
* @default undefined
|
||||
*/
|
||||
@property({ type: Number })
|
||||
min?: number;
|
||||
|
||||
/**
|
||||
* Min validation message.
|
||||
* @type {boolean}
|
||||
* @attr
|
||||
* @default
|
||||
*/
|
||||
@property({ type: String, attribute: 'min-message' })
|
||||
minMessage = 'This field need more items';
|
||||
|
||||
/**
|
||||
* This is a maximum amount of selected items in this input.
|
||||
* @type {number}
|
||||
* @attr
|
||||
* @default undefined
|
||||
*/
|
||||
@property({ type: Number })
|
||||
max?: number;
|
||||
|
||||
/**
|
||||
* Max validation message.
|
||||
* @type {boolean}
|
||||
* @attr
|
||||
* @default
|
||||
*/
|
||||
@property({ type: String, attribute: 'min-message' })
|
||||
maxMessage = 'This field exceeds the allowed amount of items';
|
||||
|
||||
/**
|
||||
* Disables the input
|
||||
* @type {boolean}
|
||||
* @attr
|
||||
* @default false
|
||||
*/
|
||||
@property({ type: Boolean, reflect: true })
|
||||
disabled = false;
|
||||
|
||||
/**
|
||||
* Makes the input readonly
|
||||
* @type {boolean}
|
||||
* @attr
|
||||
* @default false
|
||||
*/
|
||||
@property({ type: Boolean, reflect: true })
|
||||
readonly = false;
|
||||
|
||||
@property({ type: Boolean })
|
||||
showLabels = true;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_DATA_TYPE_WORKSPACE_CONTEXT, (instance) => {
|
||||
const workspace = instance;
|
||||
this.observe(workspace.data, (data) => {
|
||||
const property = data?.values.find((setting) => setting.alias === 'useLabel');
|
||||
if (property) this.showLabels = property.value as boolean;
|
||||
});
|
||||
});
|
||||
|
||||
this.addValidator(
|
||||
'rangeUnderflow',
|
||||
() => this.minMessage,
|
||||
() => !!this.min && this._items.length < this.min,
|
||||
);
|
||||
this.addValidator(
|
||||
'rangeOverflow',
|
||||
() => this.maxMessage,
|
||||
() => !!this.max && this._items.length > this.max,
|
||||
);
|
||||
}
|
||||
|
||||
@state()
|
||||
private _items: Array<UmbSwatchDetails> = [];
|
||||
|
||||
@property({ type: Array })
|
||||
public get items(): Array<UmbSwatchDetails> {
|
||||
return this._items;
|
||||
}
|
||||
public set items(items: Array<UmbSwatchDetails>) {
|
||||
this._items = items ?? [];
|
||||
this.#prevalueSorter.setModel(this.items);
|
||||
}
|
||||
|
||||
#onAdd() {
|
||||
this._items = [...this._items, { value: '', label: '' }];
|
||||
this.pristine = false;
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
this.#focusNewItem();
|
||||
}
|
||||
|
||||
#onChange(event: UmbInputEvent, currentIndex: number) {
|
||||
event.stopPropagation();
|
||||
const target = event.currentTarget as UmbMultipleColorPickerItemInputElement;
|
||||
const value = target.value as string;
|
||||
const label = target.label as string;
|
||||
|
||||
this.items = this._items.map((item, index) => (index === currentIndex ? { value, label } : item));
|
||||
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
async #focusNewItem() {
|
||||
await this.updateComplete;
|
||||
const items = this.shadowRoot?.querySelectorAll(
|
||||
'umb-multiple-color-picker-item-input',
|
||||
) as NodeListOf<UmbMultipleColorPickerItemInputElement>;
|
||||
const newItem = items[items.length - 1];
|
||||
newItem.focus();
|
||||
}
|
||||
|
||||
#deleteItem(event: UmbDeleteEvent, itemIndex: number) {
|
||||
event.stopPropagation();
|
||||
this._items = this._items.filter((item, index) => index !== itemIndex);
|
||||
this.pristine = false;
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
protected getFormElement() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<div id="sorter-wrapper">${this.#renderItems()}</div>
|
||||
${this.#renderAddButton()} `;
|
||||
}
|
||||
|
||||
#renderItems() {
|
||||
return html`
|
||||
${repeat(
|
||||
this._items,
|
||||
(item) => item.value,
|
||||
(item, index) =>
|
||||
html` <umb-multiple-color-picker-item-input
|
||||
?showLabels=${this.showLabels}
|
||||
value=${item.value}
|
||||
data-sort-entry-id=${item.value}
|
||||
label=${ifDefined(item.label)}
|
||||
name="item-${index}"
|
||||
@change=${(event: UmbChangeEvent) => this.#onChange(event, index)}
|
||||
@delete="${(event: UmbDeleteEvent) => this.#deleteItem(event, index)}"
|
||||
?disabled=${this.disabled}
|
||||
?readonly=${this.readonly}
|
||||
required
|
||||
required-message="Item ${index + 1} is missing a value"></umb-multiple-color-picker-item-input>`,
|
||||
)}
|
||||
`;
|
||||
}
|
||||
|
||||
#renderAddButton() {
|
||||
return html`
|
||||
${this.disabled || this.readonly
|
||||
? nothing
|
||||
: html`<uui-button
|
||||
id="action"
|
||||
label=${this.localize.term('general_add')}
|
||||
look="placeholder"
|
||||
color="default"
|
||||
@click="${this.#onAdd}"
|
||||
?disabled=${this.disabled}></uui-button>`}
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
css`
|
||||
#action {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.--umb-sorter-placeholder {
|
||||
position: relative;
|
||||
visibility: hidden;
|
||||
}
|
||||
.--umb-sorter-placeholder::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0px;
|
||||
border-radius: var(--uui-border-radius);
|
||||
border: 1px dashed var(--uui-color-divider-emphasis);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbMultipleColorPickerInputElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-multiple-color-picker-input': UmbMultipleColorPickerInputElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
import { css, html, nothing, customElement, property, query, ifDefined } from '@umbraco-cms/backoffice/external/lit';
|
||||
import {
|
||||
FormControlMixin,
|
||||
UUIColorPickerElement,
|
||||
UUIInputElement,
|
||||
UUIInputEvent,
|
||||
} from '@umbraco-cms/backoffice/external/uui';
|
||||
import {
|
||||
UmbModalManagerContext,
|
||||
UMB_MODAL_MANAGER_CONTEXT_TOKEN,
|
||||
UMB_CONFIRM_MODAL,
|
||||
} from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbChangeEvent, UmbInputEvent, UmbDeleteEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
|
||||
/**
|
||||
* @element umb-multiple-color-picker-item-input
|
||||
*/
|
||||
@customElement('umb-multiple-color-picker-item-input')
|
||||
export class UmbMultipleColorPickerItemInputElement extends FormControlMixin(UmbLitElement) {
|
||||
/**
|
||||
* Disables the input
|
||||
* @type {boolean}
|
||||
* @attr
|
||||
* @default false
|
||||
*/
|
||||
@property({ type: Boolean, reflect: true })
|
||||
disabled = false;
|
||||
|
||||
/**
|
||||
* Disables the input
|
||||
* @type {boolean}
|
||||
* @attr
|
||||
* @default false
|
||||
*/
|
||||
@property({ type: Boolean, reflect: true })
|
||||
readonly = false;
|
||||
|
||||
@property({ type: String })
|
||||
label?: string;
|
||||
|
||||
@query('#input')
|
||||
protected _input?: UUIInputElement;
|
||||
|
||||
@query('#color')
|
||||
protected _colorPicker!: UUIColorPickerElement;
|
||||
|
||||
private _modalContext?: UmbModalManagerContext;
|
||||
|
||||
@property({ type: Boolean })
|
||||
showLabels = true;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_MODAL_MANAGER_CONTEXT_TOKEN, (instance) => {
|
||||
this._modalContext = instance;
|
||||
});
|
||||
}
|
||||
|
||||
#onDelete() {
|
||||
const modalContext = this._modalContext?.open(UMB_CONFIRM_MODAL, {
|
||||
data: {
|
||||
headline: `${this.localize.term('actions_delete')} ${this.value || ''}`,
|
||||
content: this.localize.term('content_nestedContentDeleteItem'),
|
||||
color: 'danger',
|
||||
confirmLabel: this.localize.term('actions_delete'),
|
||||
}
|
||||
});
|
||||
|
||||
modalContext?.onSubmit().then(() => {
|
||||
this.dispatchEvent(new UmbDeleteEvent());
|
||||
});
|
||||
}
|
||||
|
||||
#onLabelInput(event: UUIInputEvent) {
|
||||
event.stopPropagation();
|
||||
this.label = event.target.value as string;
|
||||
this.dispatchEvent(new UmbInputEvent());
|
||||
}
|
||||
|
||||
#onLabelChange(event: UUIInputEvent) {
|
||||
event.stopPropagation();
|
||||
this.label = event.target.value as string;
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
#onValueChange(event: UUIInputEvent) {
|
||||
event.stopPropagation();
|
||||
this.value = event.target.value;
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
#onValueInput(event: UUIInputEvent) {
|
||||
event.stopPropagation();
|
||||
this.value = event.target.value;
|
||||
this.dispatchEvent(new UmbInputEvent());
|
||||
}
|
||||
|
||||
#onColorInput(event: InputEvent) {
|
||||
event.stopPropagation();
|
||||
this.value = this._colorPicker.value;
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
// Prevent valid events from bubbling outside the message element
|
||||
#onValid(event: any) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
// Prevent invalid events from bubbling outside the message element
|
||||
#onInvalid(event: any) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
public async focus() {
|
||||
await this.updateComplete;
|
||||
this._input?.focus();
|
||||
}
|
||||
|
||||
protected getFormElement() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
#onColorClick() {
|
||||
this._colorPicker.click();
|
||||
}
|
||||
|
||||
render() {
|
||||
//TODO: Using native input=color element instead of uui-color-picker due to its huge size and bad adaptability as a pop up
|
||||
return html`
|
||||
<uui-form-validation-message id="validation-message" @invalid=${this.#onInvalid} @valid=${this.#onValid}>
|
||||
<div id="item">
|
||||
${this.disabled || this.readonly ? nothing : html`<uui-icon name="icon-navigation"></uui-icon>`}
|
||||
<div class="color-wrapper">
|
||||
<uui-input
|
||||
id="input"
|
||||
value=${this.value}
|
||||
label=${this.localize.term('general_value')}
|
||||
placeholder=${this.localize.term('general_value')}
|
||||
@input="${this.#onValueInput}"
|
||||
@change="${this.#onValueChange}"
|
||||
required="${this.required}"
|
||||
required-message="Value is missing">
|
||||
<uui-color-swatch
|
||||
slot="prepend"
|
||||
label=${this.value}
|
||||
value="${this.value}"
|
||||
@click=${this.#onColorClick}></uui-color-swatch>
|
||||
</uui-input>
|
||||
<input aria-hidden="${true}" type="color" id="color" value=${this.value} @input=${this.#onColorInput} />
|
||||
</div>
|
||||
${this.showLabels
|
||||
? html` <uui-input
|
||||
label=${this.localize.term('placeholders_label')}
|
||||
placeholder=${this.localize.term('placeholders_label')}
|
||||
value=${ifDefined(this.label)}
|
||||
@input="${this.#onLabelInput}"
|
||||
@change="${this.#onLabelChange}"
|
||||
?disabled=${this.disabled}
|
||||
?readonly=${this.readonly}></uui-input>`
|
||||
: nothing}
|
||||
${this.readonly
|
||||
? nothing
|
||||
: html`<uui-button
|
||||
label="${this.localize.term('actions_delete')} ${this.value}"
|
||||
look="primary"
|
||||
color="danger"
|
||||
@click="${this.#onDelete}"
|
||||
?disabled=${this.disabled}
|
||||
compact>
|
||||
<uui-icon name="icon-trash"></uui-icon>
|
||||
</uui-button>`}
|
||||
</div>
|
||||
</uui-form-validation-message>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
css`
|
||||
:host {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: var(--uui-size-space-3);
|
||||
gap: var(--uui-size-space-3);
|
||||
}
|
||||
|
||||
#item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: var(--uui-size-1);
|
||||
align-items: center;
|
||||
}
|
||||
uui-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
uui-color-swatch {
|
||||
padding: var(--uui-size-1);
|
||||
}
|
||||
|
||||
.color-wrapper {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
uui-input,
|
||||
#validation-message {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
input[type='color'] {
|
||||
visibility: hidden;
|
||||
width: 0px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbMultipleColorPickerItemInputElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-multiple-color-picker-item-input': UmbMultipleColorPickerItemInputElement;
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ export const manifest: ManifestPropertyEditorSchema = {
|
||||
alias: 'items',
|
||||
label: 'Colors',
|
||||
description: 'Add, remove or sort colors',
|
||||
propertyEditorUiAlias: 'Umb.PropertyEditorUi.ColorPicker',
|
||||
propertyEditorUiAlias: 'Umb.PropertyEditorUi.ColorEditor',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const manifest: ManifestPropertyEditorUi = {
|
||||
type: 'propertyEditorUi',
|
||||
alias: 'Umb.PropertyEditorUi.ColorEditor',
|
||||
name: 'Color Editor Property Editor UI',
|
||||
js: () => import('./property-editor-ui-color-editor.element.js'),
|
||||
meta: {
|
||||
label: 'Color Editor',
|
||||
icon: 'icon-page-add',
|
||||
group: 'Umbraco.DropDown.Flexible',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import type { UmbSwatchDetails } from '@umbraco-cms/backoffice/models';
|
||||
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
|
||||
import { UmbMultipleColorPickerInputElement } from '@umbraco-cms/backoffice/components';
|
||||
|
||||
/**
|
||||
* @element umb-property-editor-ui-color-editor
|
||||
*/
|
||||
@customElement('umb-property-editor-ui-color-editor')
|
||||
export class UmbPropertyEditorUIColorEditorElement extends UmbLitElement implements UmbPropertyEditorUiElement {
|
||||
#defaultShowLabels = true;
|
||||
|
||||
@property({ type: Array })
|
||||
value: Array<UmbSwatchDetails> = [];
|
||||
|
||||
@state()
|
||||
private _showLabels = this.#defaultShowLabels;
|
||||
|
||||
@property({ attribute: false })
|
||||
public set config(config: UmbPropertyEditorConfigCollection | undefined) {
|
||||
this._showLabels = config?.getValueByAlias('useLabel') ?? this.#defaultShowLabels;
|
||||
this.value = config?.getValueByAlias('items') ?? [];
|
||||
}
|
||||
|
||||
#onChange(event: CustomEvent) {
|
||||
this.value = (event.target as UmbMultipleColorPickerInputElement).items;
|
||||
this.dispatchEvent(new CustomEvent('property-value-change'));
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<umb-multiple-color-picker-input
|
||||
?showLabels=${this._showLabels}
|
||||
.items="${this.value ?? []}"
|
||||
@change=${this.#onChange}></umb-multiple-color-picker-input>`;
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbPropertyEditorUIColorEditorElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-property-editor-ui-color-editor': UmbPropertyEditorUIColorEditorElement;
|
||||
}
|
||||
}
|
||||
@@ -103,17 +103,17 @@ export class UmbPropertyEditorUIImageCropsConfigurationElement
|
||||
<form @submit=${this.#onSubmit}>
|
||||
<div class="input">
|
||||
<uui-label for="alias">Alias</uui-label>
|
||||
<uui-input id="alias" name="alias" type="text" autocomplete="false" value=""></uui-input>
|
||||
<uui-input label="Alias" id="alias" name="alias" type="text" autocomplete="false" value=""></uui-input>
|
||||
</div>
|
||||
<div class="input">
|
||||
<uui-label for="width">Width</uui-label>
|
||||
<uui-input id="width" name="width" type="number" autocomplete="false" value="">
|
||||
<uui-input label="Width" id="width" name="width" type="number" autocomplete="false" value="">
|
||||
<span class="append" slot="append">px</span>
|
||||
</uui-input>
|
||||
</div>
|
||||
<div class="input">
|
||||
<uui-label for="height">Height</uui-label>
|
||||
<uui-input id="height" name="height" type="number" autocomplete="false" value="">
|
||||
<uui-input label="Height" id="height" name="height" type="number" autocomplete="false" value="">
|
||||
<span class="append" slot="append">px</span>
|
||||
</uui-input>
|
||||
</div>
|
||||
@@ -130,8 +130,8 @@ export class UmbPropertyEditorUIImageCropsConfigurationElement
|
||||
<span class="crop-alias">${item.alias}</span>
|
||||
<span class="crop-size">(${item.width} x ${item.height}px)</span>
|
||||
<div class="crop-actions">
|
||||
<uui-button @click=${() => this.#onEdit(item)}>Edit</uui-button>
|
||||
<uui-button color="danger" @click=${() => this.#onRemove(item.alias)}>Remove</uui-button>
|
||||
<uui-button label="Edit" @click=${() => this.#onEdit(item)}>Edit</uui-button>
|
||||
<uui-button label="Remove" color="danger" @click=${() => this.#onRemove(item.alias)}>Remove</uui-button>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { manifest as colorPicker } from './color-picker/manifests.js';
|
||||
import { manifest as colorEditor } from './color-editor/manifests.js';
|
||||
import { manifest as datePicker } from './date-picker/manifests.js';
|
||||
import { manifest as eyeDropper } from './eye-dropper/manifests.js';
|
||||
import { manifest as multiUrlPicker } from './multi-url-picker/manifests.js';
|
||||
@@ -34,6 +35,7 @@ import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/extension
|
||||
|
||||
export const manifests: Array<ManifestPropertyEditorUi> = [
|
||||
colorPicker,
|
||||
colorEditor,
|
||||
datePicker,
|
||||
eyeDropper,
|
||||
multiUrlPicker,
|
||||
|
||||
@@ -15,7 +15,7 @@ export class UmbPropertyEditorUITinyMceMaxImageSizeConfigurationElement
|
||||
value: number = 0;
|
||||
|
||||
render() {
|
||||
return html`<uui-input type="number" placeholder="Max size" .value=${this.value}></uui-input>`;
|
||||
return html`<uui-input label="Max size" type="number" placeholder="Max size" .value=${this.value}></uui-input>`;
|
||||
}
|
||||
|
||||
static styles = [UmbTextStyles];
|
||||
|
||||
@@ -691,6 +691,7 @@ export class UmbSorterController<T> implements UmbController {
|
||||
if (movingItemIndex !== -1 && movingItemIndex <= movingItemIndex) {
|
||||
newIndex--;
|
||||
}
|
||||
|
||||
if (nextEl) {
|
||||
// We had a reference element, we want to get the index of it.
|
||||
// This is might a problem if a item is being moved forward? (was also like this in the AngularJS version...)
|
||||
|
||||
@@ -39,6 +39,14 @@ export class UmbVariantId {
|
||||
return this.segment || '';
|
||||
}
|
||||
|
||||
public isCultureInvariant(): boolean {
|
||||
return this.culture === null;
|
||||
}
|
||||
|
||||
public isSegmentInvariant(): boolean {
|
||||
return this.segment === null;
|
||||
}
|
||||
|
||||
public isInvariant(): boolean {
|
||||
return this.culture === null && this.segment === null;
|
||||
}
|
||||
|
||||
@@ -219,31 +219,12 @@ export class UmbDocumentRepository
|
||||
return { error };
|
||||
}
|
||||
|
||||
async saveAndPublish(id: string, item: UpdateDocumentRequestModel, variantIds: Array<UmbVariantId>) {
|
||||
if (!id) throw new Error('id is missing');
|
||||
if (!variantIds) throw new Error('variant IDs are missing');
|
||||
//await this.#init;
|
||||
|
||||
await this.save(id, item);
|
||||
|
||||
const { error } = await this.#detailDataSource.saveAndPublish(id, variantIds);
|
||||
|
||||
if (!error) {
|
||||
// TODO: Update other stores based on above effect.
|
||||
|
||||
const notification = { data: { message: `Document saved and published` } };
|
||||
this.#notificationContext?.peek('positive', notification);
|
||||
}
|
||||
|
||||
return { error };
|
||||
}
|
||||
|
||||
async publish(id: string, variantIds: Array<UmbVariantId>) {
|
||||
if (!id) throw new Error('id is missing');
|
||||
if (!variantIds) throw new Error('variant IDs are missing');
|
||||
await this.#init;
|
||||
|
||||
const { error } = await this.#detailDataSource.saveAndPublish(id, variantIds);
|
||||
const { error } = await this.#detailDataSource.publish(id, variantIds);
|
||||
|
||||
if (!error) {
|
||||
// TODO: Update other stores based on above effect.
|
||||
|
||||
@@ -124,12 +124,14 @@ export class UmbDocumentServerDataSource
|
||||
* @return {*}
|
||||
* @memberof UmbDocumentServerDataSource
|
||||
*/
|
||||
async saveAndPublish(id: string, variantIds: Array<UmbVariantId>) {
|
||||
async publish(id: string, variantIds: Array<UmbVariantId>) {
|
||||
if (!id) throw new Error('Id is missing');
|
||||
|
||||
// TODO: THIS DOES NOT TAKE SEGMENTS INTO ACCOUNT!!!!!!
|
||||
const requestBody: PublishDocumentRequestModel = {
|
||||
cultures: variantIds.map((variant) => variant.toCultureString()),
|
||||
cultures: variantIds
|
||||
.map((variant) => (variant.isCultureInvariant() ? null : variant.toCultureString()))
|
||||
.filter((x) => x !== null) as Array<string>,
|
||||
};
|
||||
|
||||
return tryExecuteAndNotify(this.#host, DocumentResource.putDocumentByIdPublish({ id, requestBody }));
|
||||
|
||||
@@ -14,6 +14,6 @@ export class UmbDocumentSaveAndPublishWorkspaceAction extends UmbWorkspaceAction
|
||||
//const document = this.workspaceContext.getData();
|
||||
// TODO: handle errors
|
||||
//if (!document) return;
|
||||
this.workspaceContext.publish();
|
||||
this.workspaceContext.saveAndPublish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,9 +187,8 @@ export class UmbDocumentWorkspaceContext
|
||||
}
|
||||
}
|
||||
|
||||
async save() {
|
||||
if (!this.#currentData.value) return;
|
||||
if (!this.#currentData.value.id) return;
|
||||
async #createOrSave() {
|
||||
if (!this.#currentData.value?.id) throw new Error('Id is missing');
|
||||
|
||||
if (this.getIsNew()) {
|
||||
// TODO: typescript hack until we get the create type
|
||||
@@ -200,6 +199,10 @@ export class UmbDocumentWorkspaceContext
|
||||
} else {
|
||||
await this.repository.save(this.#currentData.value.id, this.#currentData.value);
|
||||
}
|
||||
}
|
||||
|
||||
async save() {
|
||||
await this.#createOrSave();
|
||||
|
||||
this.saveComplete(this.getData());
|
||||
}
|
||||
@@ -211,6 +214,19 @@ export class UmbDocumentWorkspaceContext
|
||||
}
|
||||
}
|
||||
|
||||
public async saveAndPublish() {
|
||||
await this.#createOrSave();
|
||||
// TODO: This might be right to publish all, but we need a method that just saves and publishes a declared range of variants.
|
||||
const currentData = this.#currentData.value;
|
||||
if (currentData) {
|
||||
const variantIds = currentData.variants?.map((x) => UmbVariantId.Create(x));
|
||||
const id = currentData.id;
|
||||
if (variantIds && id) {
|
||||
await this.repository.publish(id, variantIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async publish() {
|
||||
// TODO: This might be right to publish all, but we need a method that just publishes a declared range of variants.
|
||||
const currentData = this.#currentData.value;
|
||||
@@ -223,18 +239,6 @@ export class UmbDocumentWorkspaceContext
|
||||
}
|
||||
}
|
||||
|
||||
public async saveAndPublish() {
|
||||
// TODO: This might be right to publish all, but we need a method that just saves and publishes a declared range of variants.
|
||||
const currentData = this.#currentData.value;
|
||||
if (currentData) {
|
||||
const variantIds = currentData.variants?.map((x) => UmbVariantId.Create(x));
|
||||
const id = currentData.id;
|
||||
if (variantIds && id) {
|
||||
await this.repository.saveAndPublish(id, currentData, variantIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async unpublish() {
|
||||
// TODO: This might be right to unpublish all, but we need a method that just publishes a declared range of variants.
|
||||
const currentData = this.#currentData.value;
|
||||
|
||||
Reference in New Issue
Block a user