Merge branch 'main' into feature-Folder-Repository-Base
This commit is contained in:
@@ -3,7 +3,7 @@ import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
|
||||
export const manifests: Array<ManifestTypes> = [
|
||||
{
|
||||
type: 'dashboard',
|
||||
name: 'Example Dataset Workspace View',
|
||||
name: 'Example Dataset Dashboard',
|
||||
alias: 'example.dashboard.dataset',
|
||||
element: () => import('./dataset-dashboard.js'),
|
||||
weight: 900,
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Property Dataset Dashboard Example
|
||||
|
||||
This example demonstrates the how to setup the Sorter.
|
||||
|
||||
This example can still NOT sort between two groups. This will come later.
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
export const manifests: Array<ManifestTypes> = [
|
||||
{
|
||||
type: 'dashboard',
|
||||
name: 'Example Sorter Dashboard',
|
||||
alias: 'example.dashboard.dataset',
|
||||
element: () => import('./sorter-dashboard.js'),
|
||||
weight: 900,
|
||||
meta: {
|
||||
label: 'Sorter example',
|
||||
pathname: 'sorter-example',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,72 @@
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { css, html, customElement, LitElement } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
|
||||
import type { ModelEntryType } from './sorter-group.js';
|
||||
|
||||
import './sorter-group.js';
|
||||
@customElement('example-sorter-dashboard')
|
||||
export class ExampleSorterDashboard extends UmbElementMixin(LitElement) {
|
||||
groupOneItems: ModelEntryType[] = [
|
||||
{
|
||||
name: 'Apple',
|
||||
},
|
||||
{
|
||||
name: 'Banana',
|
||||
},
|
||||
{
|
||||
name: 'Pear',
|
||||
},
|
||||
{
|
||||
name: 'Pineapple',
|
||||
},
|
||||
{
|
||||
name: 'Lemon',
|
||||
},
|
||||
];
|
||||
|
||||
groupTwoItems: ModelEntryType[] = [
|
||||
{
|
||||
name: 'DXP',
|
||||
},
|
||||
{
|
||||
name: 'H5YR',
|
||||
},
|
||||
{
|
||||
name: 'UUI',
|
||||
},
|
||||
];
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<uui-box class="uui-text">
|
||||
<div class="outer-wrapper">
|
||||
<h5>Notice this example still only support single group of Sorter.</h5>
|
||||
<example-sorter-group .items=${this.groupOneItems}></example-sorter-group>
|
||||
<example-sorter-group .items=${this.groupTwoItems}></example-sorter-group>
|
||||
</div>
|
||||
</uui-box>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
padding: var(--uui-size-layout-1);
|
||||
}
|
||||
|
||||
.outer-wrapper {
|
||||
display: flex;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default ExampleSorterDashboard;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'example-sorter-dashboard': ExampleSorterDashboard;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { css, html, customElement, LitElement, repeat, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
|
||||
import { UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter';
|
||||
|
||||
import './sorter-item.js';
|
||||
import ExampleSorterItem from './sorter-item.js';
|
||||
|
||||
export type ModelEntryType = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
const SORTER_CONFIG: UmbSorterConfig<ModelEntryType, ExampleSorterItem> = {
|
||||
compareElementToModel: (element, model) => {
|
||||
return element.name === model.name;
|
||||
},
|
||||
querySelectModelToElement: (container, modelEntry) => {
|
||||
return container.querySelector("example-sorter-item[name='" + modelEntry.name + "']");
|
||||
},
|
||||
identifier: 'string-that-identifies-all-example-sorters',
|
||||
itemSelector: 'example-sorter-item',
|
||||
containerSelector: '.sorter-container',
|
||||
};
|
||||
|
||||
@customElement('example-sorter-group')
|
||||
export class ExampleSorterGroup extends UmbElementMixin(LitElement) {
|
||||
@property({ type: Array, attribute: false })
|
||||
public get items(): ModelEntryType[] {
|
||||
return this._items;
|
||||
}
|
||||
public set items(value: ModelEntryType[]) {
|
||||
this._items = value;
|
||||
this.#sorter.setModel(this._items);
|
||||
}
|
||||
private _items: ModelEntryType[] = [];
|
||||
|
||||
#sorter = new UmbSorterController<ModelEntryType, ExampleSorterItem>(this, {
|
||||
...SORTER_CONFIG,
|
||||
/*performItemInsert: ({ item, newIndex }) => {
|
||||
const oldValue = this._items;
|
||||
//console.log('inserted', item.name, 'at', newIndex, ' ', this._items.map((x) => x.name).join(', '));
|
||||
const newItems = [...this._items];
|
||||
newItems.splice(newIndex, 0, item);
|
||||
this.items = newItems;
|
||||
this.requestUpdate('_items', oldValue);
|
||||
return true;
|
||||
},
|
||||
performItemRemove: ({ item }) => {
|
||||
const oldValue = this._items;
|
||||
//console.log('removed', item.name, 'at', indexToMove, ' ', this._items.map((x) => x.name).join(', '));
|
||||
const indexToMove = this._items.findIndex((x) => x.name === item.name);
|
||||
const newItems = [...this._items];
|
||||
newItems.splice(indexToMove, 1);
|
||||
this.items = newItems;
|
||||
this.requestUpdate('_items', oldValue);
|
||||
return true;
|
||||
},
|
||||
performItemMove: ({ item, newIndex, oldIndex }) => {
|
||||
const oldValue = this._items;
|
||||
//console.log('move', item.name, 'from', oldIndex, 'to', newIndex, ' ', this._items.map((x) => x.name).join(', '));
|
||||
const newItems = [...this._items];
|
||||
newItems.splice(oldIndex, 1);
|
||||
if (oldIndex <= newIndex) {
|
||||
newIndex--;
|
||||
}
|
||||
newItems.splice(newIndex, 0, item);
|
||||
this.items = newItems;
|
||||
this.requestUpdate('_items', oldValue);
|
||||
return true;
|
||||
},*/
|
||||
onChange: ({ model }) => {
|
||||
const oldValue = this._items;
|
||||
this._items = model;
|
||||
this.requestUpdate('_items', oldValue);
|
||||
},
|
||||
});
|
||||
|
||||
removeItem = (item: ModelEntryType) => {
|
||||
this.items = this._items.filter((r) => r.name !== item.name);
|
||||
};
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="sorter-container">
|
||||
${repeat(
|
||||
this.items,
|
||||
(item) => item.name,
|
||||
(item) =>
|
||||
html`<example-sorter-item name=${item.name}>
|
||||
<button @click=${() => this.removeItem(item)}>Delete</button>
|
||||
</example-sorter-item>`,
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sorter-placeholder {
|
||||
opacity: 0.2;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default ExampleSorterGroup;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'example-sorter-group': ExampleSorterGroup;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { css, html, customElement, LitElement, state, repeat, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
|
||||
import { UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter';
|
||||
|
||||
@customElement('example-sorter-item')
|
||||
export class ExampleSorterItem extends UmbElementMixin(LitElement) {
|
||||
@property({ type: String, reflect: true })
|
||||
name: string = '';
|
||||
|
||||
@property({ type: Boolean, reflect: true, attribute: 'drag-placeholder' })
|
||||
umbDragPlaceholder = false;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
${this.name}
|
||||
<img src="https://picsum.photos/seed/${this.name}/400/400" style="width:120px;" />
|
||||
<slot></slot>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--uui-size-layout-1);
|
||||
border: 1px solid var(--uui-color-border);
|
||||
border-radius: var(--uui-border-radius);
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
:host([drag-placeholder]) {
|
||||
opacity: 0.2;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default ExampleSorterItem;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'example-sorter-item': ExampleSorterItem;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,6 @@
|
||||
"./localization": "./dist-cms/packages/core/localization/index.js",
|
||||
"./macro": "./dist-cms/packages/core/macro/index.js",
|
||||
"./menu": "./dist-cms/packages/core/menu/index.js",
|
||||
"./meta": "./dist-cms/packages/core/meta/index.js",
|
||||
"./modal": "./dist-cms/packages/core/modal/index.js",
|
||||
"./notification": "./dist-cms/packages/core/notification/index.js",
|
||||
"./picker-input": "./dist-cms/packages/core/picker-input/index.js",
|
||||
|
||||
@@ -28,7 +28,7 @@ export class UmbInputSliderElement extends FormControlMixin(UmbLitElement) {
|
||||
|
||||
#onChange(e: UUISliderEvent) {
|
||||
e.stopPropagation();
|
||||
super.value = e.target.value;
|
||||
this.value = e.target.value;
|
||||
this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
|
||||
}
|
||||
|
||||
|
||||
@@ -193,8 +193,6 @@ export class UmbInputTinyMceElement extends FormControlMixin(UmbLitElement) {
|
||||
this._tinyConfig = {
|
||||
autoresize_bottom_margin: 10,
|
||||
body_class: 'umb-rte',
|
||||
//see https://www.tiny.cloud/docs/tinymce/6/editor-important-options/#cache_suffix
|
||||
cache_suffix: `?umb__rnd=${umbMeta.clientVersion}`,
|
||||
contextMenu: false,
|
||||
inline_boundaries_selector: 'a[href],code,.mce-annotation,.umb-embed-holder,.umb-macro-holder',
|
||||
menubar: false,
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { UmbInputDocumentPickerRootElement } from '@umbraco-cms/backoffice/document';
|
||||
import { html, customElement, property, css, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { html, customElement, property, css, state, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { FormControlMixin, UUISelectEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { UmbInputMediaElement } from '@umbraco-cms/backoffice/media';
|
||||
//import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
|
||||
export type UmbTreePickerSource = {
|
||||
type?: UmbTreePickerSourceType;
|
||||
@@ -65,25 +64,22 @@ export class UmbInputTreePickerSourceElement extends FormControlMixin(UmbLitElem
|
||||
];
|
||||
|
||||
#onTypeChange(event: UUISelectEvent) {
|
||||
//console.log('onTypeChange');
|
||||
event.stopPropagation();
|
||||
|
||||
this.type = event.target.value as UmbTreePickerSource['type'];
|
||||
|
||||
this.nodeId = '';
|
||||
|
||||
// TODO: Appears that the event gets bubbled up. Will need to review. [LK]
|
||||
//this.dispatchEvent(new UmbChangeEvent());
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
#onIdChange(event: CustomEvent) {
|
||||
//console.log('onIdChange', event.target);
|
||||
switch (this.type) {
|
||||
case 'content':
|
||||
this.nodeId = (<UmbInputDocumentPickerRootElement>event.target).nodeId;
|
||||
break;
|
||||
case 'media':
|
||||
this.nodeId = (<UmbInputMediaElement>event.target).selectedIds.join('');
|
||||
break;
|
||||
case 'member':
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -103,11 +99,9 @@ export class UmbInputTreePickerSourceElement extends FormControlMixin(UmbLitElem
|
||||
case 'content':
|
||||
return this.#renderTypeContent();
|
||||
case 'media':
|
||||
return this.#renderTypeMedia();
|
||||
case 'member':
|
||||
return this.#renderTypeMember();
|
||||
default:
|
||||
return 'No type found';
|
||||
return nothing;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,18 +111,6 @@ export class UmbInputTreePickerSourceElement extends FormControlMixin(UmbLitElem
|
||||
.nodeId=${this.nodeId}></umb-input-document-picker-root>`;
|
||||
}
|
||||
|
||||
#renderTypeMedia() {
|
||||
const nodeId = this.nodeId ? [this.nodeId] : [];
|
||||
//TODO => MediaTypes
|
||||
return html`<umb-input-media @change=${this.#onIdChange} .selectedIds=${nodeId} max="1"></umb-input-media>`;
|
||||
}
|
||||
|
||||
#renderTypeMember() {
|
||||
const nodeId = this.nodeId ? [this.nodeId] : [];
|
||||
//TODO => Members
|
||||
return html`<umb-input-member @change=${this.#onIdChange} .selectedIds=${nodeId} max="1"></umb-input-member>`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
css`
|
||||
:host {
|
||||
|
||||
@@ -21,7 +21,7 @@ const SORTER_CONFIG: UmbSorterConfig<UmbSwatchDetails> = {
|
||||
return element.getAttribute('data-sort-entry-id') === model.value;
|
||||
},
|
||||
querySelectModelToElement: (container: HTMLElement, modelEntry: UmbSwatchDetails) => {
|
||||
return container.querySelector('data-sort-entry-id=[' + modelEntry.value + ']');
|
||||
return container.querySelector('[data-sort-entry-id=' + modelEntry.value + ']');
|
||||
},
|
||||
identifier: 'Umb.SorterIdentifier.ColorEditor',
|
||||
itemSelector: 'umb-multiple-color-picker-item-input',
|
||||
@@ -35,21 +35,10 @@ const SORTER_CONFIG: UmbSorterConfig<UmbSwatchDetails> = {
|
||||
export class UmbMultipleColorPickerInputElement extends FormControlMixin(UmbLitElement) {
|
||||
#sorter = 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;
|
||||
onChange: ({ model }) => {
|
||||
const oldValue = this._items;
|
||||
this._items = model;
|
||||
this.requestUpdate('_items', oldValue);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -150,7 +139,7 @@ export class UmbMultipleColorPickerInputElement extends FormControlMixin(UmbLitE
|
||||
}
|
||||
|
||||
#onAdd() {
|
||||
this._items = [...this._items, { value: '', label: '' }];
|
||||
this.items = [...this._items, { value: '', label: '' }];
|
||||
this.pristine = false;
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
this.#focusNewItem();
|
||||
@@ -180,7 +169,7 @@ export class UmbMultipleColorPickerInputElement extends FormControlMixin(UmbLitE
|
||||
|
||||
#deleteItem(event: UmbDeleteEvent, itemIndex: number) {
|
||||
event.stopPropagation();
|
||||
this._items = this._items.filter((item, index) => index !== itemIndex);
|
||||
this.items = this._items.filter((item, index) => index !== itemIndex);
|
||||
this.pristine = false;
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ const SORTER_CONFIG: UmbSorterConfig<MultipleTextStringValueItem> = {
|
||||
return element.getAttribute('data-sort-entry-id') === model.value;
|
||||
},
|
||||
querySelectModelToElement: (container: HTMLElement, modelEntry: MultipleTextStringValueItem) => {
|
||||
return container.querySelector('data-sort-entry-id=[' + modelEntry.value + ']');
|
||||
return container.querySelector('[data-sort-entry-id=' + modelEntry.value + ']');
|
||||
},
|
||||
identifier: 'Umb.SorterIdentifier.ColorEditor',
|
||||
itemSelector: 'umb-input-multiple-text-string-item',
|
||||
@@ -30,21 +30,10 @@ const SORTER_CONFIG: UmbSorterConfig<MultipleTextStringValueItem> = {
|
||||
export class UmbInputMultipleTextStringElement 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;
|
||||
onChange: ({ model }) => {
|
||||
const oldValue = this._items;
|
||||
this._items = model;
|
||||
this.requestUpdate('_items', oldValue);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -39,12 +39,12 @@ export class UmbPropertyTypeBasedPropertyElement extends UmbLitElement {
|
||||
await this._dataTypeDetailRepository.byUnique(dataTypeUnique),
|
||||
(dataType) => {
|
||||
this._dataTypeData = dataType?.values;
|
||||
this._propertyEditorUiAlias = dataType?.propertyEditorUiAlias || undefined;
|
||||
this._propertyEditorUiAlias = dataType?.editorUiAlias || undefined;
|
||||
// If there is no UI, we will look up the Property editor model to find the default UI alias:
|
||||
if (!this._propertyEditorUiAlias && dataType?.propertyEditorAlias) {
|
||||
//use 'dataType.propertyEditorAlias' to look up the extension in the registry:
|
||||
if (!this._propertyEditorUiAlias && dataType?.editorAlias) {
|
||||
//use 'dataType.editorAlias' to look up the extension in the registry:
|
||||
this.observe(
|
||||
umbExtensionsRegistry.getByTypeAndAlias('propertyEditorSchema', dataType.propertyEditorAlias),
|
||||
umbExtensionsRegistry.getByTypeAndAlias('propertyEditorSchema', dataType.editorAlias),
|
||||
(extension) => {
|
||||
if (!extension) return;
|
||||
this._propertyEditorUiAlias = extension?.meta.defaultPropertyEditorUiAlias;
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { UMB_DATATYPE_WORKSPACE_MODAL } from '../../index.js';
|
||||
import { css, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import {
|
||||
UmbModalRouteRegistrationController,
|
||||
UMB_DATA_TYPE_PICKER_FLOW_MODAL,
|
||||
UMB_WORKSPACE_MODAL,
|
||||
} from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalRouteRegistrationController, UMB_DATA_TYPE_PICKER_FLOW_MODAL } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
|
||||
// Note: Does only support picking a single data type. But this could be developed later into this same component. To follow other picker input components.
|
||||
@@ -49,9 +46,7 @@ export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.#editDataTypeModal = new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL).onSetup(() => {
|
||||
return { data: { entityType: 'data-type', preset: {} } };
|
||||
});
|
||||
this.#editDataTypeModal = new UmbModalRouteRegistrationController(this, UMB_DATATYPE_WORKSPACE_MODAL);
|
||||
|
||||
new UmbModalRouteRegistrationController(this, UMB_DATA_TYPE_PICKER_FLOW_MODAL)
|
||||
.onSetup(() => {
|
||||
|
||||
@@ -28,8 +28,8 @@ export class UmbRefDataTypeElement extends UmbElementMixin(UUIRefNodeElement) {
|
||||
(dataType) => {
|
||||
if (dataType) {
|
||||
this.name = dataType.name ?? '';
|
||||
this.propertyEditorUiAlias = dataType.propertyEditorUiAlias ?? '';
|
||||
this.propertyEditorSchemaAlias = dataType.propertyEditorAlias ?? '';
|
||||
this.propertyEditorUiAlias = dataType.editorUiAlias ?? '';
|
||||
this.propertyEditorSchemaAlias = dataType.editorAlias ?? '';
|
||||
}
|
||||
},
|
||||
'dataType',
|
||||
|
||||
@@ -4,7 +4,6 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import {
|
||||
UMB_DATA_TYPE_PICKER_FLOW_DATA_TYPE_PICKER_MODAL,
|
||||
UMB_WORKSPACE_MODAL,
|
||||
UmbDataTypePickerFlowModalData,
|
||||
UmbDataTypePickerFlowModalValue,
|
||||
UmbModalBaseElement,
|
||||
@@ -13,6 +12,7 @@ import {
|
||||
} from '@umbraco-cms/backoffice/modal';
|
||||
import { ManifestPropertyEditorUi, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UmbEntityTreeItemModel } from '@umbraco-cms/backoffice/tree';
|
||||
import { UMB_DATATYPE_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/data-type';
|
||||
|
||||
interface GroupedItems<T> {
|
||||
[key: string]: Array<T>;
|
||||
@@ -73,7 +73,7 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement<
|
||||
this.requestUpdate('_dataTypePickerModalRouteBuilder');
|
||||
});
|
||||
|
||||
this._createDataTypeModal = new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL)
|
||||
this._createDataTypeModal = new UmbModalRouteRegistrationController(this, UMB_DATATYPE_WORKSPACE_MODAL)
|
||||
.addAdditionalPath(':uiAlias')
|
||||
.onSetup((params) => {
|
||||
return { data: { entityType: 'data-type', preset: { editorUiAlias: params.uiAlias } } };
|
||||
|
||||
@@ -40,8 +40,8 @@ export class UmbDataTypeServerDataSource implements UmbDetailDataSource<UmbDataT
|
||||
unique: UmbId.new(),
|
||||
parentUnique,
|
||||
name: '',
|
||||
propertyEditorAlias: undefined,
|
||||
propertyEditorUiAlias: null,
|
||||
editorAlias: undefined,
|
||||
editorUiAlias: null,
|
||||
values: [],
|
||||
};
|
||||
|
||||
@@ -69,8 +69,8 @@ export class UmbDataTypeServerDataSource implements UmbDetailDataSource<UmbDataT
|
||||
unique: data.id,
|
||||
parentUnique: data.parentId || null,
|
||||
name: data.name,
|
||||
propertyEditorAlias: data.editorAlias,
|
||||
propertyEditorUiAlias: data.editorUiAlias || null,
|
||||
editorAlias: data.editorAlias,
|
||||
editorUiAlias: data.editorUiAlias || null,
|
||||
values: data.values as Array<UmbDataTypePropertyModel>,
|
||||
};
|
||||
|
||||
@@ -86,15 +86,15 @@ export class UmbDataTypeServerDataSource implements UmbDetailDataSource<UmbDataT
|
||||
async create(dataType: UmbDataTypeDetailModel) {
|
||||
if (!dataType) throw new Error('Data Type is missing');
|
||||
if (!dataType.unique) throw new Error('Data Type unique is missing');
|
||||
if (!dataType.propertyEditorAlias) throw new Error('Property Editor Alias is missing');
|
||||
if (!dataType.editorAlias) throw new Error('Property Editor Alias is missing');
|
||||
|
||||
// TODO: make data mapper to prevent errors
|
||||
const requestBody: CreateDataTypeRequestModel = {
|
||||
id: dataType.unique,
|
||||
parentId: dataType.parentUnique,
|
||||
name: dataType.name,
|
||||
editorAlias: dataType.propertyEditorAlias,
|
||||
editorUiAlias: dataType.propertyEditorUiAlias,
|
||||
editorAlias: dataType.editorAlias,
|
||||
editorUiAlias: dataType.editorUiAlias,
|
||||
values: dataType.values,
|
||||
};
|
||||
|
||||
@@ -121,13 +121,13 @@ export class UmbDataTypeServerDataSource implements UmbDetailDataSource<UmbDataT
|
||||
*/
|
||||
async update(data: UmbDataTypeDetailModel) {
|
||||
if (!data.unique) throw new Error('Unique is missing');
|
||||
if (!data.propertyEditorAlias) throw new Error('Property Editor Alias is missing');
|
||||
if (!data.editorAlias) throw new Error('Property Editor Alias is missing');
|
||||
|
||||
// TODO: make data mapper to prevent errors
|
||||
const requestBody: DataTypeModelBaseModel = {
|
||||
name: data.name,
|
||||
editorAlias: data.propertyEditorAlias,
|
||||
editorUiAlias: data.propertyEditorUiAlias,
|
||||
editorAlias: data.editorAlias,
|
||||
editorUiAlias: data.editorUiAlias,
|
||||
values: data.values,
|
||||
};
|
||||
|
||||
|
||||
@@ -21,9 +21,7 @@ export class UmbDataTypeDetailStore extends UmbDetailStoreBase<UmbDataTypeDetail
|
||||
|
||||
withPropertyEditorUiAlias(propertyEditorUiAlias: string) {
|
||||
// TODO: Use a model for the data-type tree items: ^^Most likely it should be parsed to the UmbEntityTreeStore as a generic type.
|
||||
return this._data.asObservablePart((items) =>
|
||||
items.filter((item) => item.propertyEditorUiAlias === propertyEditorUiAlias),
|
||||
);
|
||||
return this._data.asObservablePart((items) => items.filter((item) => item.editorUiAlias === propertyEditorUiAlias));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ export interface UmbDataTypeDetailModel {
|
||||
unique: string;
|
||||
parentUnique: string | null;
|
||||
name: string;
|
||||
propertyEditorAlias: string | undefined;
|
||||
propertyEditorUiAlias: string | null;
|
||||
editorAlias: string | undefined;
|
||||
editorUiAlias: string | null;
|
||||
values: Array<UmbDataTypePropertyModel>;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ export class UmbDataTypeWorkspaceContext
|
||||
readonly name = this.#data.asObservablePart((data) => data?.name);
|
||||
readonly unique = this.#data.asObservablePart((data) => data?.unique);
|
||||
|
||||
readonly propertyEditorUiAlias = this.#data.asObservablePart((data) => data?.propertyEditorUiAlias);
|
||||
readonly propertyEditorSchemaAlias = this.#data.asObservablePart((data) => data?.propertyEditorAlias);
|
||||
readonly propertyEditorUiAlias = this.#data.asObservablePart((data) => data?.editorUiAlias);
|
||||
readonly propertyEditorSchemaAlias = this.#data.asObservablePart((data) => data?.editorAlias);
|
||||
|
||||
#properties = new UmbArrayState<PropertyEditorConfigProperty>([], (x) => x.alias);
|
||||
readonly properties = this.#properties.asObservable();
|
||||
@@ -231,10 +231,10 @@ export class UmbDataTypeWorkspaceContext
|
||||
}
|
||||
|
||||
setPropertyEditorSchemaAlias(alias?: string) {
|
||||
this.#data.update({ propertyEditorAlias: alias });
|
||||
this.#data.update({ editorAlias: alias });
|
||||
}
|
||||
setPropertyEditorUiAlias(alias?: string) {
|
||||
this.#data.update({ propertyEditorUiAlias: alias });
|
||||
this.#data.update({ editorUiAlias: alias });
|
||||
}
|
||||
|
||||
async propertyValueByAlias<ReturnType = unknown>(propertyAlias: string) {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { UmbDataTypeDetailModel } from '../types.js';
|
||||
import { UmbModalToken, UmbWorkspaceData, UmbWorkspaceValue } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export const UMB_DATATYPE_WORKSPACE_MODAL = new UmbModalToken<
|
||||
UmbWorkspaceData<UmbDataTypeDetailModel>,
|
||||
UmbWorkspaceValue
|
||||
>('Umb.Modal.Workspace', {
|
||||
modal: {
|
||||
type: 'sidebar',
|
||||
size: 'large',
|
||||
},
|
||||
data: { entityType: 'data-type', preset: {} },
|
||||
// Recast the type, so the entityType data prop is not required:
|
||||
}) as UmbModalToken<Omit<UmbWorkspaceData<UmbDataTypeDetailModel>, 'entityType'>, UmbWorkspaceValue>;
|
||||
@@ -1 +1,2 @@
|
||||
export * from './data-type-workspace.context-token.js';
|
||||
export * from './data-type-workspace.modal-token.js';
|
||||
|
||||
@@ -41,11 +41,11 @@ export class UmbWorkspaceViewDataTypeInfoElement extends UmbLitElement implement
|
||||
<div slot="editor">${this._dataType?.unique}</div>
|
||||
</umb-property-layout>
|
||||
<umb-property-layout label="Property Editor Alias">
|
||||
<div slot="editor">${this._dataType?.propertyEditorAlias}</div>
|
||||
<div slot="editor">${this._dataType?.editorAlias}</div>
|
||||
</umb-property-layout>
|
||||
|
||||
<umb-property-layout label="Property Editor UI Alias">
|
||||
<div slot="editor">${this._dataType?.propertyEditorUiAlias}</div>
|
||||
<div slot="editor">${this._dataType?.editorUiAlias}</div>
|
||||
</umb-property-layout>
|
||||
</uui-box>
|
||||
`;
|
||||
|
||||
@@ -17,7 +17,6 @@ export * from './extension-registry/index.js';
|
||||
export * from './id/index.js';
|
||||
export * from './macro/index.js';
|
||||
export * from './menu/index.js';
|
||||
export * from './meta/index.js';
|
||||
export * from './modal/index.js';
|
||||
export * from './notification/index.js';
|
||||
export * from './picker-input/index.js';
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import packageJson from '../../../../package.json';
|
||||
|
||||
export const umbMeta = {
|
||||
name: 'Bellissima',
|
||||
clientName: packageJson.name,
|
||||
clientVersion: packageJson.version,
|
||||
};
|
||||
@@ -1,10 +1,7 @@
|
||||
import { CreateDataTypeRequestModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
// TODO: Change model:
|
||||
export interface UmbWorkspaceData {
|
||||
export interface UmbWorkspaceData<DataModelType = unknown> {
|
||||
entityType: string;
|
||||
preset: Partial<CreateDataTypeRequestModel>;
|
||||
preset: Partial<DataModelType>;
|
||||
}
|
||||
|
||||
// TODO: It would be good with a WorkspaceValueBaseType, to avoid the hardcoded type for unique here:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { type UmbTreePickerSource, UmbInputTreePickerSourceElement } from '@umbraco-cms/backoffice/components';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
|
||||
import { type UmbPropertyEditorConfigCollection, UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
|
||||
@@ -25,7 +25,7 @@ export class UmbPropertyEditorUITreePickerSourcePickerElement extends UmbLitElem
|
||||
dynamicRoot: target.dynamicRoot,
|
||||
};
|
||||
|
||||
this.dispatchEvent(new CustomEvent('property-value-change'));
|
||||
this.dispatchEvent(new UmbPropertyValueChangeEvent());
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
import { UmbInputDocumentTypeElement } from '@umbraco-cms/backoffice/document-type';
|
||||
import { UmbInputMediaTypeElement } from '@umbraco-cms/backoffice/media-type';
|
||||
import { UmbMemberTypeInputElement } from '@umbraco-cms/backoffice/member-type';
|
||||
import { UmbInputMemberTypeElement } from '@umbraco-cms/backoffice/member-type';
|
||||
import type { UmbTreePickerSource } from '@umbraco-cms/backoffice/components';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
|
||||
import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor';
|
||||
|
||||
/**
|
||||
* @element umb-property-editor-ui-tree-picker-source-type-picker
|
||||
*/
|
||||
@customElement('umb-property-editor-ui-tree-picker-source-type-picker')
|
||||
export class UmbPropertyEditorUITreePickerSourceTypePickerElement extends UmbLitElement implements UmbPropertyEditorUiElement {
|
||||
export class UmbPropertyEditorUITreePickerSourceTypePickerElement
|
||||
extends UmbLitElement
|
||||
implements UmbPropertyEditorUiElement
|
||||
{
|
||||
#datasetContext?: typeof UMB_PROPERTY_DATASET_CONTEXT.TYPE;
|
||||
|
||||
@property({ type: Array })
|
||||
@@ -42,7 +46,7 @@ export class UmbPropertyEditorUITreePickerSourceTypePickerElement extends UmbLit
|
||||
// If we had a sourceType before, we can see this as a change and not the initial value,
|
||||
// so let's reset the value, so we don't carry over content-types to the new source type.
|
||||
if (this.#initialized && this.sourceType !== startNode.type) {
|
||||
this.value = [];
|
||||
this.#setValue([]);
|
||||
}
|
||||
|
||||
this.sourceType = startNode.type;
|
||||
@@ -59,19 +63,22 @@ export class UmbPropertyEditorUITreePickerSourceTypePickerElement extends UmbLit
|
||||
#onChange(event: CustomEvent) {
|
||||
switch (this.sourceType) {
|
||||
case 'content':
|
||||
this.value = (<UmbInputDocumentTypeElement>event.target).selectedIds;
|
||||
this.#setValue((<UmbInputDocumentTypeElement>event.target).selectedIds);
|
||||
break;
|
||||
case 'media':
|
||||
this.value = (<UmbInputMediaTypeElement>event.target).selectedIds;
|
||||
this.#setValue((<UmbInputMediaTypeElement>event.target).selectedIds);
|
||||
break;
|
||||
case 'member':
|
||||
this.value = (<UmbMemberTypeInputElement>event.target).selectedIds;
|
||||
this.#setValue((<UmbInputMemberTypeElement>event.target).selectedIds);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.dispatchEvent(new CustomEvent('property-value-change'));
|
||||
#setValue(value: string[]) {
|
||||
this.value = value;
|
||||
this.dispatchEvent(new UmbPropertyValueChangeEvent());
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -87,7 +94,7 @@ export class UmbPropertyEditorUITreePickerSourceTypePickerElement extends UmbLit
|
||||
case 'member':
|
||||
return this.#renderTypeMember();
|
||||
default:
|
||||
return 'No source type found';
|
||||
return html`<p>No source type found</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { type UmbPropertyEditorConfigCollection, UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type { UmbInputTreeElement } from '@umbraco-cms/backoffice/tree';
|
||||
import type { UmbTreePickerSource } from '@umbraco-cms/backoffice/components';
|
||||
|
||||
@@ -44,8 +44,8 @@ export class UmbPropertyEditorUITreePickerElement extends UmbLitElement implemen
|
||||
this.startNodeId = startNode.id;
|
||||
}
|
||||
|
||||
this.min = config?.getValueByAlias('minNumber') || 0;
|
||||
this.max = config?.getValueByAlias('maxNumber') || 0;
|
||||
this.min = Number(config?.getValueByAlias('minNumber')) || 0;
|
||||
this.max = Number(config?.getValueByAlias('maxNumber')) || 0;
|
||||
|
||||
this.filter = config?.getValueByAlias('filter');
|
||||
this.showOpenButton = config?.getValueByAlias('showOpenButton');
|
||||
@@ -54,7 +54,7 @@ export class UmbPropertyEditorUITreePickerElement extends UmbLitElement implemen
|
||||
|
||||
#onChange(e: CustomEvent) {
|
||||
this.value = (e.target as UmbInputTreeElement).value as string;
|
||||
this.dispatchEvent(new CustomEvent('property-value-change'));
|
||||
this.dispatchEvent(new UmbPropertyValueChangeEvent());
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -61,27 +61,23 @@ function destroyPreventEvent(element: Element) {
|
||||
element.removeAttribute('draggable');
|
||||
}
|
||||
|
||||
type INTERNAL_UmbSorterConfig<T> = {
|
||||
compareElementToModel: (el: HTMLElement, modelEntry: T) => boolean;
|
||||
querySelectModelToElement: (container: HTMLElement, modelEntry: T) => HTMLElement | null;
|
||||
type INTERNAL_UmbSorterConfig<T, ElementType extends HTMLElement> = {
|
||||
compareElementToModel: (el: ElementType, modelEntry: T) => boolean;
|
||||
querySelectModelToElement: (container: HTMLElement, modelEntry: T) => ElementType | null;
|
||||
identifier: string;
|
||||
itemSelector: string;
|
||||
disabledItemSelector?: string;
|
||||
containerSelector: string;
|
||||
ignorerSelector: string;
|
||||
placeholderClass: string;
|
||||
placeholderClass?: string;
|
||||
placeholderAttr?: string;
|
||||
draggableSelector?: string;
|
||||
boundarySelector?: string;
|
||||
dataTransferResolver?: (dataTransfer: DataTransfer | null, currentItem: T) => void;
|
||||
onStart?: (argument: { item: T; element: HTMLElement }) => void;
|
||||
onChange?: (argument: { item: T; element: HTMLElement }) => void;
|
||||
onContainerChange?: (argument: { item: T; element: HTMLElement }) => void;
|
||||
onEnd?: (argument: { item: T; element: HTMLElement }) => void;
|
||||
onSync?: (argument: {
|
||||
item: T;
|
||||
fromController: UmbSorterController<T>;
|
||||
toController: UmbSorterController<T>;
|
||||
}) => void;
|
||||
onStart?: (argument: { item: T; element: ElementType }) => void;
|
||||
onChange?: (argument: { item: T; model: Array<T> }) => void;
|
||||
onContainerChange?: (argument: { item: T; element: ElementType }) => void;
|
||||
onEnd?: (argument: { item: T; element: ElementType }) => void;
|
||||
itemHasNestedContainersResolver?: (element: HTMLElement) => boolean;
|
||||
onDisallowed?: () => void;
|
||||
onAllowed?: () => void;
|
||||
@@ -90,23 +86,24 @@ type INTERNAL_UmbSorterConfig<T> = {
|
||||
containerElement: Element;
|
||||
containerRect: DOMRect;
|
||||
item: T;
|
||||
element: HTMLElement;
|
||||
element: ElementType;
|
||||
elementRect: DOMRect;
|
||||
relatedElement: HTMLElement;
|
||||
relatedElement: ElementType;
|
||||
relatedRect: DOMRect;
|
||||
placeholderIsInThisRow: boolean;
|
||||
horizontalPlaceAfter: boolean;
|
||||
}) => void;
|
||||
performItemMove?: (argument: { item: T; newIndex: number; oldIndex: number }) => Promise<boolean> | boolean;
|
||||
performItemInsert?: (argument: { item: T; newIndex: number }) => Promise<boolean> | boolean;
|
||||
performItemRemove?: (argument: { item: T }) => Promise<boolean> | boolean;
|
||||
};
|
||||
|
||||
// External type with some properties optional, as they have defaults:
|
||||
export type UmbSorterConfig<T> = Omit<
|
||||
INTERNAL_UmbSorterConfig<T>,
|
||||
'placeholderClass' | 'ignorerSelector' | 'containerSelector'
|
||||
export type UmbSorterConfig<T, ElementType extends HTMLElement = HTMLElement> = Omit<
|
||||
INTERNAL_UmbSorterConfig<T, ElementType>,
|
||||
'ignorerSelector' | 'containerSelector'
|
||||
> &
|
||||
Partial<Pick<INTERNAL_UmbSorterConfig<T>, 'placeholderClass' | 'ignorerSelector' | 'containerSelector'>>;
|
||||
Partial<Pick<INTERNAL_UmbSorterConfig<T, ElementType>, 'ignorerSelector' | 'containerSelector'>>;
|
||||
|
||||
/**
|
||||
* @export
|
||||
@@ -114,55 +111,58 @@ export type UmbSorterConfig<T> = Omit<
|
||||
* @implements {UmbControllerInterface}
|
||||
* @description This controller can make user able to sort items.
|
||||
*/
|
||||
export class UmbSorterController<T> implements UmbController {
|
||||
export class UmbSorterController<T, ElementType extends HTMLElement = HTMLElement> implements UmbController {
|
||||
#host;
|
||||
#config: INTERNAL_UmbSorterConfig<T>;
|
||||
#config: INTERNAL_UmbSorterConfig<T, ElementType>;
|
||||
#observer;
|
||||
|
||||
#model: Array<T> = [];
|
||||
#rqaId?: number;
|
||||
|
||||
#containerElement!: HTMLElement;
|
||||
#currentContainerVM = this;
|
||||
|
||||
#currentContainerCtrl: UmbSorterController<T, ElementType> = this;
|
||||
#currentContainerElement: Element | null = null;
|
||||
#useContainerShadowRoot?: boolean;
|
||||
|
||||
#scrollElement?: Element | null;
|
||||
#currentElement?: HTMLElement;
|
||||
#currentElement?: ElementType;
|
||||
#currentDragElement?: Element;
|
||||
#currentDragRect?: DOMRect;
|
||||
#currentItem?: T | null;
|
||||
#currentItem?: T;
|
||||
|
||||
#currentIndex?: number;
|
||||
#dragX = 0;
|
||||
#dragY = 0;
|
||||
|
||||
private _lastIndicationContainerVM: UmbSorterController<T> | null = null;
|
||||
#lastIndicationContainerCtrl: UmbSorterController<T, ElementType> | null = null;
|
||||
|
||||
public get controllerAlias() {
|
||||
return this.#config.identifier;
|
||||
}
|
||||
|
||||
constructor(host: UmbControllerHostElement, config: UmbSorterConfig<T>) {
|
||||
constructor(host: UmbControllerHostElement, config: UmbSorterConfig<T, ElementType>) {
|
||||
this.#host = host;
|
||||
|
||||
// Set defaults:
|
||||
config.ignorerSelector ??= 'a, img, iframe';
|
||||
config.placeholderClass ??= '--umb-sorter-placeholder';
|
||||
if (!config.placeholderClass && !config.placeholderAttr) {
|
||||
config.placeholderAttr = 'drag-placeholder';
|
||||
}
|
||||
|
||||
this.#config = config as INTERNAL_UmbSorterConfig<T>;
|
||||
this.#config = config as INTERNAL_UmbSorterConfig<T, ElementType>;
|
||||
host.addController(this);
|
||||
|
||||
//this.#currentContainerElement = host;
|
||||
|
||||
this.#observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
mutation.addedNodes.forEach((addedNode) => {
|
||||
if ((addedNode as HTMLElement).matches && (addedNode as HTMLElement).matches(this.#config.itemSelector)) {
|
||||
this.setupItem(addedNode as HTMLElement);
|
||||
this.setupItem(addedNode as ElementType);
|
||||
}
|
||||
});
|
||||
mutation.removedNodes.forEach((removedNode) => {
|
||||
if ((removedNode as HTMLElement).matches && (removedNode as HTMLElement).matches(this.#config.itemSelector)) {
|
||||
this.destroyItem(removedNode as HTMLElement);
|
||||
this.destroyItem(removedNode as ElementType);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -185,7 +185,9 @@ export class UmbSorterController<T> implements UmbController {
|
||||
? this.#host.shadowRoot!.querySelector(this.#config.containerSelector)
|
||||
: this.#host) ?? this.#host;
|
||||
|
||||
if (this.#currentContainerElement === this.#containerElement) {
|
||||
this.#useContainerShadowRoot = this.#containerElement === this.#host;
|
||||
|
||||
if (!this.#currentContainerElement || this.#currentContainerElement === this.#containerElement) {
|
||||
this.#currentContainerElement = containerEl;
|
||||
}
|
||||
this.#containerElement = containerEl as HTMLElement;
|
||||
@@ -198,10 +200,13 @@ export class UmbSorterController<T> implements UmbController {
|
||||
// TODO: Clean up??
|
||||
this.#observer.disconnect();
|
||||
|
||||
const containerElement = this.#containerElement.shadowRoot ?? this.#containerElement;
|
||||
// Only look at the shadowRoot if the containerElement is host.
|
||||
const containerElement = this.#useContainerShadowRoot
|
||||
? this.#containerElement.shadowRoot ?? this.#containerElement
|
||||
: this.#containerElement;
|
||||
containerElement.querySelectorAll(this.#config.itemSelector).forEach((child) => {
|
||||
if (child.matches && child.matches(this.#config.itemSelector)) {
|
||||
this.setupItem(child as HTMLElement);
|
||||
this.setupItem(child as ElementType);
|
||||
}
|
||||
});
|
||||
this.#observer.observe(containerElement, {
|
||||
@@ -219,14 +224,22 @@ export class UmbSorterController<T> implements UmbController {
|
||||
}
|
||||
}
|
||||
|
||||
setupItem(element: HTMLElement) {
|
||||
setupItem(element: ElementType) {
|
||||
if (this.#config.ignorerSelector) {
|
||||
setupIgnorerElements(element, this.#config.ignorerSelector);
|
||||
}
|
||||
|
||||
if (!this.#config.disabledItemSelector || !element.matches(this.#config.disabledItemSelector)) {
|
||||
element.draggable = true;
|
||||
element.addEventListener('dragstart', this.handleDragStart);
|
||||
element.addEventListener('dragstart', this.#handleDragStart);
|
||||
}
|
||||
|
||||
// If we have a currentItem and the element matches, we should set the currentElement to this element.
|
||||
if (this.#currentItem && this.#config.compareElementToModel(element, this.#currentItem)) {
|
||||
if (this.#currentElement !== element) {
|
||||
console.log('THIS ACTUALLY HAPPENED... NOTICE THIS!');
|
||||
this.#setCurrentElement(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,27 +248,28 @@ export class UmbSorterController<T> implements UmbController {
|
||||
destroyIgnorerElements(element, this.#config.ignorerSelector);
|
||||
}
|
||||
|
||||
element.removeEventListener('dragstart', this.handleDragStart);
|
||||
element.removeEventListener('dragstart', this.#handleDragStart);
|
||||
}
|
||||
|
||||
handleDragStart = (event: DragEvent) => {
|
||||
if (this.#currentElement) {
|
||||
this.handleDragEnd();
|
||||
#setupPlaceholderStyle() {
|
||||
if (this.#config.placeholderClass) {
|
||||
this.#currentElement?.classList.add(this.#config.placeholderClass);
|
||||
}
|
||||
if (this.#config.placeholderAttr) {
|
||||
this.#currentElement?.setAttribute(this.#config.placeholderAttr, '');
|
||||
}
|
||||
}
|
||||
#removePlaceholderStyle() {
|
||||
if (this.#config.placeholderClass) {
|
||||
this.#currentElement?.classList.remove(this.#config.placeholderClass);
|
||||
}
|
||||
if (this.#config.placeholderAttr) {
|
||||
this.#currentElement?.removeAttribute(this.#config.placeholderAttr);
|
||||
}
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.effectAllowed = 'move'; // copyMove when we enhance the drag with clipboard data.
|
||||
event.dataTransfer.dropEffect = 'none'; // visual feedback when dropped.
|
||||
}
|
||||
|
||||
if (!this.#scrollElement) {
|
||||
this.#scrollElement = getParentScrollElement(this.#containerElement, true);
|
||||
}
|
||||
|
||||
const element = (event.target as HTMLElement).closest(this.#config.itemSelector);
|
||||
|
||||
if (!element) return;
|
||||
#setCurrentElement(element: ElementType) {
|
||||
this.#currentElement = element;
|
||||
|
||||
this.#currentDragElement = this.#config.draggableSelector
|
||||
? element.querySelector(this.#config.draggableSelector) ?? undefined
|
||||
@@ -270,90 +284,88 @@ export class UmbSorterController<T> implements UmbController {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#currentElement = element as HTMLElement;
|
||||
this.#currentDragRect = this.#currentDragElement.getBoundingClientRect();
|
||||
this.#currentItem = this.getItemOfElement(this.#currentElement);
|
||||
this.#setupPlaceholderStyle();
|
||||
}
|
||||
|
||||
#handleDragStart = (event: DragEvent) => {
|
||||
const element = (event.target as HTMLElement).closest(this.#config.itemSelector);
|
||||
if (!element) return;
|
||||
|
||||
if (this.#currentElement && this.#currentElement !== element) {
|
||||
this.#handleDragEnd();
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.effectAllowed = 'move'; // copyMove when we enhance the drag with clipboard data.
|
||||
event.dataTransfer.dropEffect = 'none'; // visual feedback when dropped.
|
||||
}
|
||||
|
||||
if (!this.#scrollElement) {
|
||||
this.#scrollElement = getParentScrollElement(this.#containerElement, true);
|
||||
}
|
||||
|
||||
this.#setCurrentElement(element as ElementType);
|
||||
this.#currentDragRect = this.#currentDragElement?.getBoundingClientRect();
|
||||
this.#currentItem = this.getItemOfElement(this.#currentElement!);
|
||||
if (!this.#currentItem) {
|
||||
console.error('Could not find item related to this element.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.#currentElement.style.transform = 'translateZ(0)'; // Solves problem with FireFox and ShadowDom in the drag-image.
|
||||
// Get the current index of the item:
|
||||
this.#currentIndex = this.#model.indexOf(this.#currentItem);
|
||||
|
||||
this.#currentElement!.style.transform = 'translateZ(0)'; // Solves problem with FireFox and ShadowDom in the drag-image.
|
||||
|
||||
if (this.#config.dataTransferResolver) {
|
||||
this.#config.dataTransferResolver(event.dataTransfer, this.#currentItem);
|
||||
}
|
||||
|
||||
if (this.#config.onStart) {
|
||||
this.#config.onStart({ item: this.#currentItem!, element: this.#currentElement });
|
||||
this.#config.onStart({ item: this.#currentItem, element: this.#currentElement! });
|
||||
}
|
||||
|
||||
window.addEventListener('dragover', this.handleDragMove);
|
||||
window.addEventListener('dragend', this.handleDragEnd);
|
||||
window.addEventListener('dragover', this.#handleDragMove);
|
||||
window.addEventListener('dragend', this.#handleDragEnd);
|
||||
|
||||
// We must wait one frame before changing the look of the block.
|
||||
this.#rqaId = requestAnimationFrame(() => {
|
||||
// It should be okay to use the same rqaId, as the move does not or is okay not to happen on first frame/drag-move.
|
||||
// It should be okay to use the same rqaId, as the move does not, or is okay not, to happen on first frame/drag-move.
|
||||
this.#rqaId = undefined;
|
||||
if (this.#currentElement) {
|
||||
this.#currentElement.style.transform = '';
|
||||
this.#currentElement.classList.add(this.#config.placeholderClass);
|
||||
this.#setupPlaceholderStyle();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleDragEnd = async () => {
|
||||
#handleDragEnd = async () => {
|
||||
window.removeEventListener('dragover', this.#handleDragMove);
|
||||
window.removeEventListener('dragend', this.#handleDragEnd);
|
||||
|
||||
if (!this.#currentElement || !this.#currentItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.removeEventListener('dragover', this.handleDragMove);
|
||||
window.removeEventListener('dragend', this.handleDragEnd);
|
||||
this.#currentElement.style.transform = '';
|
||||
this.#currentElement.classList.remove(this.#config.placeholderClass);
|
||||
this.#removePlaceholderStyle();
|
||||
|
||||
this.stopAutoScroll();
|
||||
this.#stopAutoScroll();
|
||||
this.removeAllowIndication();
|
||||
|
||||
if ((await this.#currentContainerVM.sync(this.#currentElement, this)) === false) {
|
||||
// Sync could not succeed, might be because item is not allowed here.
|
||||
|
||||
this.#currentContainerVM = this;
|
||||
if (this.#config.onContainerChange) {
|
||||
this.#config.onContainerChange({
|
||||
item: this.#currentItem,
|
||||
element: this.#currentElement,
|
||||
//ownerVM: this.#currentContainerVM.ownerVM,
|
||||
});
|
||||
}
|
||||
|
||||
// Lets move the Element back to where it came from:
|
||||
const movingItemIndex = this.#model.indexOf(this.#currentItem);
|
||||
if (movingItemIndex < this.#model.length - 1) {
|
||||
const afterItem = this.#model[movingItemIndex + 1];
|
||||
const afterEl = this.#config.querySelectModelToElement(this.#containerElement, afterItem);
|
||||
if (afterEl) {
|
||||
this.#containerElement.insertBefore(this.#currentElement, afterEl);
|
||||
} else {
|
||||
this.#containerElement.appendChild(this.#currentElement);
|
||||
}
|
||||
} else {
|
||||
this.#containerElement.appendChild(this.#currentElement);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.#config.onEnd) {
|
||||
this.#config.onEnd({ item: this.#currentItem, element: this.#currentElement });
|
||||
}
|
||||
|
||||
if (this.#rqaId) {
|
||||
cancelAnimationFrame(this.#rqaId);
|
||||
this.#rqaId = undefined;
|
||||
}
|
||||
|
||||
this.#currentContainerElement = this.#containerElement;
|
||||
this.#currentContainerVM = this;
|
||||
this.#currentContainerCtrl = this;
|
||||
|
||||
this.#rqaId = undefined;
|
||||
this.#currentItem = undefined;
|
||||
this.#currentElement = undefined;
|
||||
this.#currentDragElement = undefined;
|
||||
@@ -362,7 +374,7 @@ export class UmbSorterController<T> implements UmbController {
|
||||
this.#dragY = 0;
|
||||
};
|
||||
|
||||
handleDragMove = (event: DragEvent) => {
|
||||
#handleDragMove = (event: DragEvent) => {
|
||||
if (!this.#currentElement) {
|
||||
return;
|
||||
}
|
||||
@@ -386,13 +398,13 @@ export class UmbSorterController<T> implements UmbController {
|
||||
const insideCurrentRect = isWithinRect(this.#dragX, this.#dragY, this.#currentDragRect);
|
||||
if (!insideCurrentRect) {
|
||||
if (this.#rqaId === undefined) {
|
||||
this.#rqaId = requestAnimationFrame(this.moveCurrentElement);
|
||||
this.#rqaId = requestAnimationFrame(this.#updateDragMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
moveCurrentElement = () => {
|
||||
#updateDragMove = () => {
|
||||
this.#rqaId = undefined;
|
||||
if (!this.#currentElement || !this.#currentContainerElement || !this.#currentItem) {
|
||||
return;
|
||||
@@ -404,7 +416,9 @@ export class UmbSorterController<T> implements UmbController {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have a boundarySelector, try it, if we didn't get anything fall back to currentContainerElement.
|
||||
let toBeCurrentContainerCtrl: UmbSorterController<T, ElementType> | undefined = undefined;
|
||||
|
||||
// If we have a boundarySelector, try it. If we didn't get anything fall back to currentContainerElement:
|
||||
const currentBoundaryElement =
|
||||
(this.#config.boundarySelector
|
||||
? this.#currentContainerElement.closest(this.#config.boundarySelector)
|
||||
@@ -412,24 +426,25 @@ export class UmbSorterController<T> implements UmbController {
|
||||
|
||||
const currentBoundaryRect = currentBoundaryElement.getBoundingClientRect();
|
||||
|
||||
const currentContainerHasItems = this.#currentContainerVM.hasOtherItemsThan(this.#currentItem!);
|
||||
const currentContainerHasItems = this.#currentContainerCtrl.hasOtherItemsThan(this.#currentItem);
|
||||
|
||||
// if empty we will be move likely to accept an item (add 20px to the bounding box)
|
||||
// If we have items we must be 10 within the container to accept the move.
|
||||
// If we have items we must be 10px within the container to accept the move.
|
||||
const offsetEdge = currentContainerHasItems ? -10 : 20;
|
||||
if (!isWithinRect(this.#dragX, this.#dragY, currentBoundaryRect, offsetEdge)) {
|
||||
// we are outside the current container boundary, so lets see if there is a parent we can move.
|
||||
// we are outside the current container boundary, so lets see if there is a parent we can move to.
|
||||
|
||||
const parentNode = this.#currentContainerElement.parentNode;
|
||||
if (parentNode) {
|
||||
if (parentNode && this.#config.containerSelector) {
|
||||
// TODO: support multiple parent shadowDOMs?
|
||||
const parentContainer = this.#config.containerSelector
|
||||
? (parentNode as HTMLElement).closest(this.#config.containerSelector)
|
||||
: null;
|
||||
const parentContainer = (parentNode as ShadowRoot).host
|
||||
? (parentNode as ShadowRoot).host.closest(this.#config.containerSelector)
|
||||
: (parentNode as HTMLElement).closest(this.#config.containerSelector);
|
||||
if (parentContainer) {
|
||||
const parentContainerVM = (parentContainer as any)['__umbBlockGridSorterController']();
|
||||
if (parentContainerVM.unique === this.controllerAlias) {
|
||||
const parentContainerCtrl = (parentContainer as any)['__umbBlockGridSorterController']();
|
||||
if (parentContainerCtrl.unique === this.controllerAlias) {
|
||||
this.#currentContainerElement = parentContainer as Element;
|
||||
this.#currentContainerVM = parentContainerVM;
|
||||
toBeCurrentContainerCtrl = parentContainerCtrl;
|
||||
if (this.#config.onContainerChange) {
|
||||
this.#config.onContainerChange({
|
||||
item: this.#currentItem,
|
||||
@@ -442,12 +457,12 @@ export class UmbSorterController<T> implements UmbController {
|
||||
}
|
||||
}
|
||||
|
||||
const containerElement = this.#useContainerShadowRoot
|
||||
? this.#currentContainerElement.shadowRoot ?? this.#currentContainerElement
|
||||
: this.#currentContainerElement;
|
||||
|
||||
// We want to retrieve the children of the container, every time to ensure we got the right order and index
|
||||
const orderedContainerElements = Array.from(
|
||||
this.#currentContainerElement.shadowRoot
|
||||
? this.#currentContainerElement.shadowRoot.querySelectorAll(this.#config.itemSelector)
|
||||
: this.#currentContainerElement.querySelectorAll(this.#config.itemSelector),
|
||||
);
|
||||
const orderedContainerElements = Array.from(containerElement.querySelectorAll(this.#config.itemSelector));
|
||||
|
||||
const currentContainerRect = this.#currentContainerElement.getBoundingClientRect();
|
||||
|
||||
@@ -515,10 +530,10 @@ export class UmbSorterController<T> implements UmbController {
|
||||
// gather elements on the same row.
|
||||
const subOffsetEdge = subContainerHasItems ? -10 : 20;
|
||||
if (isWithinRect(this.#dragX, this.#dragY, subBoundaryRect, subOffsetEdge)) {
|
||||
const subVm = (subLayoutEl as any)['__umbBlockGridSorterController']();
|
||||
if (subVm.unique === this.controllerAlias) {
|
||||
const subCtrl = (subLayoutEl as any)['__umbBlockGridSorterController']();
|
||||
if (subCtrl.unique === this.controllerAlias) {
|
||||
this.#currentContainerElement = subLayoutEl as HTMLElement;
|
||||
this.#currentContainerVM = subVm;
|
||||
toBeCurrentContainerCtrl = subCtrl;
|
||||
if (this.#config.onContainerChange) {
|
||||
this.#config.onContainerChange({
|
||||
item: this.#currentItem,
|
||||
@@ -526,7 +541,7 @@ export class UmbSorterController<T> implements UmbController {
|
||||
//ownerVM: this.#currentContainerVM.ownerVM,
|
||||
});
|
||||
}
|
||||
this.moveCurrentElement();
|
||||
this.#updateDragMove();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -534,7 +549,9 @@ export class UmbSorterController<T> implements UmbController {
|
||||
}
|
||||
|
||||
// Indication if drop is good:
|
||||
if (this.updateAllowIndication(this.#currentContainerVM, this.#currentItem) === false) {
|
||||
if (
|
||||
this.updateAllowIndication(toBeCurrentContainerCtrl ?? this.#currentContainerCtrl, this.#currentItem) === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -581,162 +598,154 @@ export class UmbSorterController<T> implements UmbController {
|
||||
}
|
||||
|
||||
const foundElIndex = orderedContainerElements.indexOf(foundEl);
|
||||
const placeAt = placeAfter ? foundElIndex + 1 : foundElIndex;
|
||||
|
||||
this.move(orderedContainerElements, placeAt);
|
||||
const newIndex = placeAfter ? foundElIndex + 1 : foundElIndex;
|
||||
this.#moveElementTo(toBeCurrentContainerCtrl, newIndex);
|
||||
|
||||
return;
|
||||
}
|
||||
// We skipped the above part cause we are above or below container:
|
||||
|
||||
// Indication if drop is good:
|
||||
if (this.updateAllowIndication(this.#currentContainerVM, this.#currentItem) === false) {
|
||||
if (
|
||||
this.updateAllowIndication(toBeCurrentContainerCtrl ?? this.#currentContainerCtrl, this.#currentItem) === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.#dragY < currentContainerRect.top) {
|
||||
this.move(orderedContainerElements, 0);
|
||||
this.#moveElementTo(toBeCurrentContainerCtrl, 0);
|
||||
} else if (this.#dragY > currentContainerRect.bottom) {
|
||||
this.move(orderedContainerElements, -1);
|
||||
this.#moveElementTo(toBeCurrentContainerCtrl, -1);
|
||||
}
|
||||
};
|
||||
|
||||
move(orderedContainerElements: Array<Element>, newElIndex: number) {
|
||||
if (!this.#currentElement || !this.#currentItem || !this.#currentContainerElement) return;
|
||||
|
||||
newElIndex = newElIndex === -1 ? orderedContainerElements.length : newElIndex;
|
||||
|
||||
const containerElement = this.#currentContainerElement.shadowRoot ?? this.#currentContainerElement;
|
||||
|
||||
const placeBeforeElement = orderedContainerElements[newElIndex];
|
||||
if (placeBeforeElement) {
|
||||
// We do not need to move this, if the element to be placed before is it self.
|
||||
if (placeBeforeElement !== this.#currentElement) {
|
||||
containerElement.insertBefore(this.#currentElement, placeBeforeElement);
|
||||
}
|
||||
} else {
|
||||
containerElement.appendChild(this.#currentElement);
|
||||
async #moveElementTo(containerCtrl: UmbSorterController<T, ElementType> | undefined, newIndex: number) {
|
||||
if (!this.#currentElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.#config.onChange) {
|
||||
this.#config.onChange({
|
||||
element: this.#currentElement,
|
||||
item: this.#currentItem,
|
||||
//ownerVM: this.#currentContainerVM.ownerVM
|
||||
});
|
||||
containerCtrl ??= this as UmbSorterController<T, ElementType>;
|
||||
|
||||
// If same container and same index, do nothing:
|
||||
if (this.#currentContainerCtrl === containerCtrl && this.#currentIndex === newIndex) return;
|
||||
|
||||
if (await containerCtrl.moveItemInModel(newIndex, this.#currentElement, this.#currentContainerCtrl)) {
|
||||
this.#currentContainerCtrl = containerCtrl;
|
||||
this.#currentIndex = newIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/** Management methods: */
|
||||
|
||||
public getItemOfElement(element: HTMLElement) {
|
||||
public getItemOfElement(element: ElementType) {
|
||||
if (!element) {
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
return this.#model.find((entry: T) => this.#config.compareElementToModel(element, entry));
|
||||
}
|
||||
|
||||
public async removeItem(item: T) {
|
||||
if (!item) {
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.#config.performItemRemove) {
|
||||
return await this.#config.performItemRemove({ item });
|
||||
return (await this.#config.performItemRemove({ item })) ?? false;
|
||||
} else {
|
||||
const oldIndex = this.#model.indexOf(item);
|
||||
if (oldIndex !== -1) {
|
||||
return this.#model.splice(oldIndex, 1)[0];
|
||||
const newModel = [...this.#model];
|
||||
newModel.splice(oldIndex, 1);
|
||||
this.#model = newModel;
|
||||
this.#config.onChange?.({ model: newModel, item });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
hasOtherItemsThan(item: T) {
|
||||
public hasOtherItemsThan(item: T) {
|
||||
return this.#model.filter((x) => x !== item).length > 0;
|
||||
}
|
||||
|
||||
public async sync(element: HTMLElement, fromVm: UmbSorterController<T>) {
|
||||
const movingItem = fromVm.getItemOfElement(element);
|
||||
if (!movingItem) {
|
||||
public async moveItemInModel(newIndex: number, element: ElementType, fromCtrl: UmbSorterController<T, ElementType>) {
|
||||
const item = fromCtrl.getItemOfElement(element);
|
||||
if (!item) {
|
||||
console.error('Could not find item of sync item');
|
||||
return false;
|
||||
}
|
||||
if (this.notifyRequestDrop({ item: movingItem }) === false) {
|
||||
return false;
|
||||
}
|
||||
if (fromVm.removeItem(movingItem) === null) {
|
||||
console.error('Sync could not remove item');
|
||||
if (this.notifyRequestDrop({ item }) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Find next element, to then find the index of that element in items-data, to use as a safe reference to where the item will go in our items-data.
|
||||
* This enables the container to contain various other elements and as well having these elements change while sorting is occurring.
|
||||
*/
|
||||
const localMove = fromCtrl === this;
|
||||
|
||||
// find next valid element (This assumes the next element in DOM is presented in items-data, aka. only moving one item between each sync)
|
||||
let nextEl: Element | null = null;
|
||||
let loopEl: Element | null = element;
|
||||
while ((loopEl = loopEl?.nextElementSibling)) {
|
||||
if (loopEl.matches && loopEl.matches(this.#config.itemSelector)) {
|
||||
nextEl = loopEl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (localMove) {
|
||||
// Local move:
|
||||
|
||||
let newIndex = this.#model.length;
|
||||
// TODO: Maybe this should be replaceable/configurable:
|
||||
const oldIndex = this.#model.indexOf(item);
|
||||
|
||||
const movingItemIndex = this.#model.indexOf(movingItem);
|
||||
|
||||
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...)
|
||||
newIndex = this.#model.findIndex((entry) => this.#config.compareElementToModel(nextEl! as HTMLElement, entry));
|
||||
}
|
||||
|
||||
if (this.#config.performItemInsert) {
|
||||
const result = await this.#config.performItemInsert({ item: movingItem, newIndex });
|
||||
if (this.#config.performItemMove) {
|
||||
const result = await this.#config.performItemMove({ item, newIndex, oldIndex });
|
||||
if (result === false) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
this.#model.splice(newIndex, 0, movingItem);
|
||||
const newModel = [...this.#model];
|
||||
newModel.splice(oldIndex, 1);
|
||||
if (oldIndex <= newIndex) {
|
||||
newIndex--;
|
||||
}
|
||||
newModel.splice(newIndex, 0, item);
|
||||
this.#model = newModel;
|
||||
this.#config.onChange?.({ model: newModel, item });
|
||||
}
|
||||
} else {
|
||||
// Not a local move:
|
||||
|
||||
if ((await fromCtrl.removeItem(item)) !== true) {
|
||||
console.error('Sync could not remove item');
|
||||
return false;
|
||||
}
|
||||
|
||||
const eventData = { item: movingItem, fromController: fromVm, toController: this };
|
||||
if (fromVm !== this) {
|
||||
fromVm.notifySync(eventData);
|
||||
if (this.#config.performItemInsert) {
|
||||
const result = await this.#config.performItemInsert({ item, newIndex });
|
||||
if (result === false) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const newModel = [...this.#model];
|
||||
newModel.splice(newIndex, 0, item);
|
||||
this.#model = newModel;
|
||||
this.#config.onChange?.({ model: newModel, item });
|
||||
}
|
||||
}
|
||||
this.notifySync(eventData);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
updateAllowIndication(contextVM: UmbSorterController<T>, item: T) {
|
||||
updateAllowIndication(controller: UmbSorterController<T, ElementType>, item: T) {
|
||||
// Remove old indication:
|
||||
if (this._lastIndicationContainerVM !== null && this._lastIndicationContainerVM !== contextVM) {
|
||||
this._lastIndicationContainerVM.notifyAllowed();
|
||||
if (this.#lastIndicationContainerCtrl !== null && this.#lastIndicationContainerCtrl !== controller) {
|
||||
this.#lastIndicationContainerCtrl.notifyAllowed();
|
||||
}
|
||||
this._lastIndicationContainerVM = contextVM;
|
||||
this.#lastIndicationContainerCtrl = controller;
|
||||
|
||||
if (contextVM.notifyRequestDrop({ item: item }) === true) {
|
||||
contextVM.notifyAllowed();
|
||||
if (controller.notifyRequestDrop({ item: item }) === true) {
|
||||
controller.notifyAllowed();
|
||||
return true;
|
||||
}
|
||||
|
||||
contextVM.notifyDisallowed(); // This block is not accepted to we will indicate that its not allowed.
|
||||
controller.notifyDisallowed(); // This block is not accepted to we will indicate that its not allowed.
|
||||
return false;
|
||||
}
|
||||
removeAllowIndication() {
|
||||
// Remove old indication:
|
||||
if (this._lastIndicationContainerVM !== null) {
|
||||
this._lastIndicationContainerVM.notifyAllowed();
|
||||
if (this.#lastIndicationContainerCtrl !== null) {
|
||||
this.#lastIndicationContainerCtrl.notifyAllowed();
|
||||
}
|
||||
this._lastIndicationContainerVM = null;
|
||||
this.#lastIndicationContainerCtrl = null;
|
||||
}
|
||||
|
||||
// TODO: Move auto scroll into its own class?
|
||||
@@ -786,24 +795,19 @@ export class UmbSorterController<T> implements UmbController {
|
||||
? -1
|
||||
: 0;
|
||||
|
||||
this.#autoScrollRAF = requestAnimationFrame(this._performAutoScroll);
|
||||
this.#autoScrollRAF = requestAnimationFrame(this.#performAutoScroll);
|
||||
}
|
||||
}
|
||||
private _performAutoScroll = () => {
|
||||
#performAutoScroll = () => {
|
||||
this.#autoScrollEl!.scrollLeft += this.autoScrollX * autoScrollSpeed;
|
||||
this.#autoScrollEl!.scrollTop += this.autoScrollY * autoScrollSpeed;
|
||||
this.#autoScrollRAF = requestAnimationFrame(this._performAutoScroll);
|
||||
this.#autoScrollRAF = requestAnimationFrame(this.#performAutoScroll);
|
||||
};
|
||||
private stopAutoScroll() {
|
||||
#stopAutoScroll() {
|
||||
cancelAnimationFrame(this.#autoScrollRAF!);
|
||||
this.#autoScrollRAF = null;
|
||||
}
|
||||
|
||||
public notifySync(data: any) {
|
||||
if (this.#config.onSync) {
|
||||
this.#config.onSync(data);
|
||||
}
|
||||
}
|
||||
public notifyDisallowed() {
|
||||
if (this.#config.onDisallowed) {
|
||||
this.#config.onDisallowed();
|
||||
@@ -824,10 +828,10 @@ export class UmbSorterController<T> implements UmbController {
|
||||
destroy() {
|
||||
// Do something when host element is destroyed.
|
||||
if (this.#currentElement) {
|
||||
this.handleDragEnd();
|
||||
this.#handleDragEnd();
|
||||
}
|
||||
|
||||
this._lastIndicationContainerVM = null;
|
||||
this.#lastIndicationContainerCtrl = null;
|
||||
|
||||
// TODO: Clean up items??
|
||||
this.#observer.disconnect();
|
||||
|
||||
@@ -51,20 +51,20 @@ The configuration has a lot of optional options, but the required ones are:
|
||||
- itemSelector
|
||||
- containerSelector
|
||||
|
||||
It can be set up as following:
|
||||
It can be set up as follows:
|
||||
|
||||
```typescript
|
||||
import { UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter';
|
||||
|
||||
type MySortEntryType = {...}
|
||||
const awesomeModel: Array<MySortEntryType> = [...]
|
||||
type MySortEntryType = {...};
|
||||
const awesomeModel: Array<MySortEntryType> = [...];
|
||||
|
||||
const MY_SORTER_CONFIG: UmbSorterConfig<MySortEntryType> = {
|
||||
compareElementToModel: (element: HTMLElement, model: MySortEntryType) => {
|
||||
return element.getAttribute('data-sort-entry-id') === model.id;
|
||||
},
|
||||
querySelectModelToElement: (container: HTMLElement, modelEntry: MySortEntryType) => {
|
||||
return container.querySelector('data-sort-entry-id=[' + modelEntry.id + ']');
|
||||
return container.querySelector('[data-sort-entry-id=' + modelEntry.id + ']');
|
||||
},
|
||||
identifier: 'test-sorter',
|
||||
itemSelector: 'li',
|
||||
@@ -101,7 +101,13 @@ const MY_SORTER_CONFIG: UmbSorterConfig<MySortEntryType> = {...}
|
||||
|
||||
|
||||
export class MyElement extends UmbElementMixin(LitElement) {
|
||||
#sorter = new UmbSorterController(this, MY_SORTER_CONFIG);
|
||||
#sorter = new UmbSorterController(this, {...MY_SORTER_CONFIG,
|
||||
onChange: ({ model }) => {
|
||||
const oldValue = this.awesomeModel;
|
||||
this.awesomeModel = model;
|
||||
this.requestUpdate('awesomeModel', oldValue);
|
||||
},
|
||||
});
|
||||
|
||||
constructor() {
|
||||
this.#sorter.setModel(awesomeModel);
|
||||
@@ -176,11 +182,13 @@ export class MyElement extends UmbElementMixin(LitElement) {
|
||||
#sorter = new UmbSorterController(this, {
|
||||
...SORTER_CONFIG,
|
||||
performItemInsert: ({ item, newIndex }) => {
|
||||
console.log(item, newIndex);
|
||||
// Perform some logic here to calculate the new sortOrder & save it.
|
||||
// Insert logic that updates the model, so the item gets the new index in the model.
|
||||
return true;
|
||||
},
|
||||
performItemRemove: () => {
|
||||
// Insert logic that updates the model, so the item gets removed from the model.
|
||||
return true;
|
||||
},
|
||||
performItemRemove: () => true,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
@@ -12,7 +12,7 @@ const SORTER_CONFIG: UmbSorterConfig<SortEntryType> = {
|
||||
return element.getAttribute('data-sort-entry-id') === model.id;
|
||||
},
|
||||
querySelectModelToElement: (container: HTMLElement, modelEntry: SortEntryType) => {
|
||||
return container.querySelector('data-sort-entry-id=[' + modelEntry.id + ']');
|
||||
return container.querySelector('[data-sort-entry-id=' + modelEntry.id + ']');
|
||||
},
|
||||
identifier: 'test-sorter',
|
||||
itemSelector: 'li',
|
||||
@@ -40,6 +40,9 @@ export default class UmbTestSorterControllerElement extends UmbLitElement {
|
||||
@state()
|
||||
private vertical = true;
|
||||
|
||||
@state()
|
||||
private _items: Array<SortEntryType> = [...model];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.sorter = new UmbSorterController(this, {
|
||||
@@ -47,6 +50,11 @@ export default class UmbTestSorterControllerElement extends UmbLitElement {
|
||||
resolveVerticalDirection: () => {
|
||||
this.vertical ? true : false;
|
||||
},
|
||||
onChange: ({ model }) => {
|
||||
const oldValue = this._items;
|
||||
this._items = model;
|
||||
this.requestUpdate('_items', oldValue);
|
||||
},
|
||||
});
|
||||
this.sorter.setModel(model);
|
||||
}
|
||||
@@ -61,7 +69,7 @@ export default class UmbTestSorterControllerElement extends UmbLitElement {
|
||||
Horizontal/Vertical
|
||||
</uui-button>
|
||||
<ul class="${this.vertical ? 'vertical' : 'horizontal'}">
|
||||
${model.map(
|
||||
${this._items.map(
|
||||
(entry) =>
|
||||
html`<li class="item" data-sort-entry-id="${entry.id}">
|
||||
<span><uui-icon name="icon-wand"></uui-icon>${entry.value}</span>
|
||||
|
||||
@@ -2,6 +2,8 @@ import { css, html, customElement, property } from '@umbraco-cms/backoffice/exte
|
||||
import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { UmbInputDocumentElement } from '@umbraco-cms/backoffice/document';
|
||||
import { UmbInputMediaElement } from '@umbraco-cms/backoffice/media';
|
||||
import { UmbInputMemberElement } from '@umbraco-cms/backoffice/member';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import type { UmbTreePickerSource } from '@umbraco-cms/backoffice/components';
|
||||
|
||||
@@ -64,7 +66,20 @@ export class UmbInputTreeElement extends FormControlMixin(UmbLitElement) {
|
||||
selectedIds: Array<string> = [];
|
||||
|
||||
#onChange(event: CustomEvent) {
|
||||
switch (this._type) {
|
||||
case 'content':
|
||||
this.value = (event.target as UmbInputDocumentElement).selectedIds.join(',');
|
||||
break;
|
||||
case 'media':
|
||||
this.value = (event.target as UmbInputMediaElement).selectedIds.join(',');
|
||||
break;
|
||||
case 'member':
|
||||
this.value = (event.target as UmbInputMemberElement).selectedIds.join(',');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
@@ -75,6 +90,17 @@ export class UmbInputTreeElement extends FormControlMixin(UmbLitElement) {
|
||||
render() {
|
||||
switch (this._type) {
|
||||
case 'content':
|
||||
return this.#renderContentPicker();
|
||||
case 'media':
|
||||
return this.#renderMediaPicker();
|
||||
case 'member':
|
||||
return this.#renderMemberPicker();
|
||||
default:
|
||||
return html`<p>Type could not be found.</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
#renderContentPicker() {
|
||||
return html`<umb-input-document
|
||||
.selectedIds=${this.selectedIds}
|
||||
.startNodeId=${this.startNodeId}
|
||||
@@ -84,17 +110,20 @@ export class UmbInputTreeElement extends FormControlMixin(UmbLitElement) {
|
||||
?showOpenButton=${this.showOpenButton}
|
||||
?ignoreUserStartNodes=${this.ignoreUserStartNodes}
|
||||
@change=${this.#onChange}></umb-input-document>`;
|
||||
case 'media':
|
||||
}
|
||||
|
||||
#renderMediaPicker() {
|
||||
return html`<umb-input-media
|
||||
.selectedIds=${this.selectedIds}
|
||||
.startNodeId=${this.startNodeId}
|
||||
.filter=${this.filter}
|
||||
.min=${this.min}
|
||||
.max=${this.max}
|
||||
?showOpenButton=${this.showOpenButton}
|
||||
?ignoreUserStartNodes=${this.ignoreUserStartNodes}
|
||||
@change=${this.#onChange}></umb-input-media>`;
|
||||
case 'member':
|
||||
}
|
||||
|
||||
#renderMemberPicker() {
|
||||
return html`<umb-input-member
|
||||
.selectedIds=${this.selectedIds}
|
||||
.filter=${this.filter}
|
||||
@@ -102,11 +131,7 @@ export class UmbInputTreeElement extends FormControlMixin(UmbLitElement) {
|
||||
.max=${this.max}
|
||||
?showOpenButton=${this.showOpenButton}
|
||||
?ignoreUserStartNodes=${this.ignoreUserStartNodes}
|
||||
@change=${this.#onChange}>
|
||||
</umb-input-member>`;
|
||||
default:
|
||||
return html`Type could not be found`;
|
||||
}
|
||||
@change=${this.#onChange}></umb-input-member>`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
import { UmbDocumentTypePickerContext } from './input-document-type.context.js';
|
||||
import { css, html, customElement, property, state, ifDefined, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import {
|
||||
css,
|
||||
html,
|
||||
customElement,
|
||||
property,
|
||||
state,
|
||||
ifDefined,
|
||||
repeat,
|
||||
nothing,
|
||||
} from '@umbraco-cms/backoffice/external/lit';
|
||||
import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import type { DocumentTypeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
@@ -95,6 +104,10 @@ export class UmbInputDocumentTypeElement extends FormControlMixin(UmbLitElement)
|
||||
.observeRouteBuilder((routeBuilder) => {
|
||||
this._editDocumentTypePath = routeBuilder({});
|
||||
});
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
this.addValidator(
|
||||
'rangeUnderflow',
|
||||
@@ -123,26 +136,47 @@ export class UmbInputDocumentTypeElement extends FormControlMixin(UmbLitElement)
|
||||
pickableFilter: (x) => x.isElement,
|
||||
});
|
||||
} else {
|
||||
this.#pickerContext.openPicker({ hideTreeRoot: true });
|
||||
this.#pickerContext.openPicker({
|
||||
hideTreeRoot: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html` <uui-ref-list>${this._items?.map((item) => this._renderItem(item))}</uui-ref-list>
|
||||
${this.#renderAddButton()}`;
|
||||
return html` ${this.#renderItems()} ${this.#renderAddButton()} `;
|
||||
}
|
||||
|
||||
#renderItems() {
|
||||
if (!this._items) return nothing;
|
||||
return html`
|
||||
<uui-ref-list
|
||||
>${repeat(
|
||||
this._items,
|
||||
(item) => item.id,
|
||||
(item) => this.#renderItem(item),
|
||||
)}</uui-ref-list
|
||||
>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderAddButton() {
|
||||
if (this.max === 1 && this.selectedIds.length === 1) return nothing;
|
||||
return html`<uui-button id="add-button" look="placeholder" @click=${this.#openPicker} label="open">
|
||||
Add
|
||||
</uui-button>`;
|
||||
if (this.max > 0 && this.selectedIds.length >= this.max) return nothing;
|
||||
return html`
|
||||
<uui-button
|
||||
id="add-button"
|
||||
look="placeholder"
|
||||
@click=${this.#openPicker}
|
||||
label="${this.localize.term('general_choose')}"
|
||||
>${this.localize.term('general_choose')}</uui-button
|
||||
>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderItem(item: DocumentTypeItemResponseModel) {
|
||||
#renderItem(item: DocumentTypeItemResponseModel) {
|
||||
if (!item.id) return;
|
||||
return html`
|
||||
<uui-ref-node-document-type name=${ifDefined(item.name)}>
|
||||
${this.#renderIcon(item)}
|
||||
<uui-action-bar slot="actions">
|
||||
<uui-button
|
||||
compact
|
||||
@@ -161,6 +195,11 @@ export class UmbInputDocumentTypeElement extends FormControlMixin(UmbLitElement)
|
||||
`;
|
||||
}
|
||||
|
||||
#renderIcon(item: DocumentTypeItemResponseModel) {
|
||||
if (!item.icon) return;
|
||||
return html`<uui-icon slot="icon" name=${item.icon}></uui-icon>`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
css`
|
||||
#add-button {
|
||||
|
||||
@@ -18,7 +18,7 @@ const SORTER_CONFIG: UmbSorterConfig<DocumentTypePropertyTypeResponseModel> = {
|
||||
return element.getAttribute('data-umb-property-id') === model.id;
|
||||
},
|
||||
querySelectModelToElement: (container: HTMLElement, modelEntry: DocumentTypePropertyTypeResponseModel) => {
|
||||
return container.querySelector('data-umb-property-id=[' + modelEntry.id + ']');
|
||||
return container.querySelector('[data-umb-property-id=' + modelEntry.id + ']');
|
||||
},
|
||||
identifier: 'content-type-property-sorter',
|
||||
itemSelector: '[data-umb-property-id]',
|
||||
@@ -45,6 +45,19 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle
|
||||
performItemRemove: (args) => {
|
||||
return this._propertyStructureHelper.removeProperty(args.item.id!);
|
||||
},
|
||||
performItemMove: (args) => {
|
||||
this._propertyStructureHelper.removeProperty(args.item.id!);
|
||||
let sortOrder = 0;
|
||||
if (this._propertyStructure.length > 0) {
|
||||
if (args.newIndex === 0) {
|
||||
sortOrder = (this._propertyStructure[0].sortOrder ?? 0) - 1;
|
||||
} else {
|
||||
sortOrder =
|
||||
(this._propertyStructure[Math.min(args.newIndex, this._propertyStructure.length - 1)].sortOrder ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
return this._propertyStructureHelper.insertProperty(args.item, sortOrder);
|
||||
},
|
||||
});
|
||||
|
||||
private _containerId: string | undefined;
|
||||
@@ -134,6 +147,7 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle
|
||||
if (isSorting) {
|
||||
this.#propertySorter.setModel(this._propertyStructure);
|
||||
} else {
|
||||
// TODO: Make a more proper way to disable sorting:
|
||||
this.#propertySorter.setModel([]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement {
|
||||
|
||||
config: UmbSorterConfig<PropertyTypeContainerModelBaseModel> = {
|
||||
...SORTER_CONFIG,
|
||||
// TODO: Missing handlers to work properly: performItemMove and performItemRemove
|
||||
performItemInsert: async (args) => {
|
||||
if (!this._groups) return false;
|
||||
const oldIndex = this._groups.findIndex((group) => group.id! === args.item.id);
|
||||
|
||||
@@ -38,6 +38,7 @@ export class UmbDocumentTypeWorkspaceViewEditElement extends UmbLitElement imple
|
||||
|
||||
config: UmbSorterConfig<PropertyTypeContainerModelBaseModel> = {
|
||||
...SORTER_CONFIG,
|
||||
// TODO: Missing handlers to work properly: performItemMove and performItemRemove
|
||||
performItemInsert: async (args) => {
|
||||
if (!this._tabs) return false;
|
||||
const oldIndex = this._tabs.findIndex((tab) => tab.id! === args.item.id);
|
||||
|
||||
@@ -60,6 +60,18 @@ export class UmbInputDocumentElement extends FormControlMixin(UmbLitElement) {
|
||||
this.#pickerContext.setSelection(ids);
|
||||
}
|
||||
|
||||
@property({ type: String })
|
||||
startNodeId?: string;
|
||||
|
||||
@property({ type: String })
|
||||
filter?: string;
|
||||
|
||||
@property({ type: Boolean })
|
||||
showOpenButton?: boolean;
|
||||
|
||||
@property({ type: Boolean })
|
||||
ignoreUserStartNodes?: boolean;
|
||||
|
||||
@property()
|
||||
public set value(idsString: string) {
|
||||
// Its with full purpose we don't call super.value, as thats being handled by the observation of the context selection.
|
||||
@@ -73,6 +85,10 @@ export class UmbInputDocumentElement extends FormControlMixin(UmbLitElement) {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
this.addValidator(
|
||||
'rangeUnderflow',
|
||||
@@ -90,23 +106,37 @@ export class UmbInputDocumentElement extends FormControlMixin(UmbLitElement) {
|
||||
this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems));
|
||||
}
|
||||
|
||||
protected _openPicker() {
|
||||
// TODO: Configure the content picker, with `startNodeId`, `filter` and `ignoreUserStartNodes` [LK]
|
||||
console.log("_openPicker", [this.startNodeId, this.filter, this.ignoreUserStartNodes]);
|
||||
this.#pickerContext.openPicker({
|
||||
hideTreeRoot: true,
|
||||
});
|
||||
}
|
||||
|
||||
protected _openItem(item: DocumentItemResponseModel) {
|
||||
// TODO: Implement the Content editing infinity editor. [LK]
|
||||
console.log('TODO: _openItem', item);
|
||||
}
|
||||
|
||||
protected getFormElement() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
${this._items
|
||||
? html` <uui-ref-list
|
||||
return html` ${this.#renderItems()} ${this.#renderAddButton()} `;
|
||||
}
|
||||
|
||||
#renderItems() {
|
||||
if (!this._items) return;
|
||||
// TODO: Add sorting. [LK]
|
||||
return html`<uui-ref-list
|
||||
>${repeat(
|
||||
this._items,
|
||||
(item) => item.id,
|
||||
(item) => this._renderItem(item),
|
||||
)}
|
||||
</uui-ref-list>`
|
||||
: ''}
|
||||
${this.#renderAddButton()}
|
||||
`;
|
||||
</uui-ref-list>`;
|
||||
}
|
||||
|
||||
#renderAddButton() {
|
||||
@@ -114,7 +144,7 @@ export class UmbInputDocumentElement extends FormControlMixin(UmbLitElement) {
|
||||
return html`<uui-button
|
||||
id="add-button"
|
||||
look="placeholder"
|
||||
@click=${() => this.#pickerContext.openPicker()}
|
||||
@click=${this._openPicker}
|
||||
label=${this.localize.term('general_choose')}></uui-button>`;
|
||||
}
|
||||
|
||||
@@ -122,8 +152,9 @@ export class UmbInputDocumentElement extends FormControlMixin(UmbLitElement) {
|
||||
if (!item.id) return;
|
||||
return html`
|
||||
<uui-ref-node name=${ifDefined(item.name)} detail=${ifDefined(item.id)}>
|
||||
<!-- TODO: implement is trashed <uui-tag size="s" slot="tag" color="danger">Trashed</uui-tag> -->
|
||||
${this._renderIsTrashed(item)}
|
||||
<uui-action-bar slot="actions">
|
||||
${this._renderOpenButton(item)}
|
||||
<uui-button
|
||||
@click=${() => this.#pickerContext.requestRemoveItem(item.id!)}
|
||||
label="Remove document ${item.name}"
|
||||
@@ -134,6 +165,18 @@ export class UmbInputDocumentElement extends FormControlMixin(UmbLitElement) {
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderIsTrashed(item: DocumentItemResponseModel) {
|
||||
if (!item.isTrashed) return;
|
||||
return html`<uui-tag size="s" slot="tag" color="danger">Trashed</uui-tag>`;
|
||||
}
|
||||
|
||||
private _renderOpenButton(item: DocumentItemResponseModel) {
|
||||
if (!this.showOpenButton) return;
|
||||
return html`<uui-button @click=${() => this._openItem(item)} label="Open document ${item.name}"
|
||||
>${this.localize.term('general_open')}</uui-button
|
||||
>`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
css`
|
||||
#add-button {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UmbMediaTypePickerContext } from './input-media-type.context.js';
|
||||
import { css, html, customElement, property, state, ifDefined } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { css, html, customElement, property, state, ifDefined, repeat } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import type { MediaTypeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
@@ -73,6 +73,10 @@ export class UmbInputMediaTypeElement extends FormControlMixin(UmbLitElement) {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
this.addValidator(
|
||||
'rangeUnderflow',
|
||||
@@ -94,33 +98,63 @@ export class UmbInputMediaTypeElement extends FormControlMixin(UmbLitElement) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
#openPicker() {
|
||||
this.#pickerContext.openPicker({
|
||||
hideTreeRoot: true,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
console.log('ITEMS', this._items);
|
||||
return html` ${this.#renderItems()} ${this.#renderAddButton()} `;
|
||||
}
|
||||
|
||||
#renderItems() {
|
||||
if (!this._items) return;
|
||||
return html`
|
||||
<uui-ref-list>${this._items?.map((item) => this._renderItem(item))}</uui-ref-list>
|
||||
<uui-button id="add-button" look="placeholder" @click=${() => this.#pickerContext.openPicker()} label="open"
|
||||
>Add</uui-button
|
||||
<uui-ref-list
|
||||
>${repeat(
|
||||
this._items,
|
||||
(item) => item.id,
|
||||
(item) => this.#renderItem(item),
|
||||
)}</uui-ref-list
|
||||
>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderItem(item: MediaTypeItemResponseModel) {
|
||||
if (!item.id) return;
|
||||
|
||||
//TODO: Using uui-ref-node as we don't have a uui-ref-media-type yet.
|
||||
#renderAddButton() {
|
||||
if (this.max > 0 && this.selectedIds.length >= this.max) return;
|
||||
return html`
|
||||
<uui-ref-node name=${ifDefined(item.name)}>
|
||||
<uui-button
|
||||
id="add-button"
|
||||
look="placeholder"
|
||||
@click=${this.#openPicker}
|
||||
label="${this.localize.term('general_choose')}"
|
||||
>${this.localize.term('general_choose')}</uui-button
|
||||
>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderItem(item: MediaTypeItemResponseModel) {
|
||||
if (!item.id) return;
|
||||
return html`
|
||||
<uui-ref-node-document-type name=${ifDefined(item.name)}>
|
||||
${this.#renderIcon(item)}
|
||||
<uui-action-bar slot="actions">
|
||||
<uui-button
|
||||
@click=${() => this.#pickerContext.requestRemoveItem(item.id!)}
|
||||
label="Remove Media Type ${item.name}"
|
||||
>Remove</uui-button
|
||||
>${this.localize.term('general_remove')}</uui-button
|
||||
>
|
||||
</uui-action-bar>
|
||||
</uui-ref-node>
|
||||
</uui-ref-node-document-type>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderIcon(item: MediaTypeItemResponseModel) {
|
||||
if (!item.icon) return;
|
||||
return html`<uui-icon slot="icon" name=${item.icon}></uui-icon>`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
css`
|
||||
#add-button {
|
||||
|
||||
@@ -18,7 +18,7 @@ const SORTER_CONFIG: UmbSorterConfig<MediaTypePropertyTypeResponseModel> = {
|
||||
return element.getAttribute('data-umb-property-id') === model.id;
|
||||
},
|
||||
querySelectModelToElement: (container: HTMLElement, modelEntry: MediaTypePropertyTypeResponseModel) => {
|
||||
return container.querySelector('data-umb-property-id=[' + modelEntry.id + ']');
|
||||
return container.querySelector('[data-umb-property-id=' + modelEntry.id + ']');
|
||||
},
|
||||
identifier: 'content-type-property-sorter',
|
||||
itemSelector: '[data-umb-property-id]',
|
||||
@@ -45,6 +45,19 @@ export class UmbMediaTypeWorkspaceViewEditPropertiesElement extends UmbLitElemen
|
||||
performItemRemove: (args) => {
|
||||
return this._propertyStructureHelper.removeProperty(args.item.id!);
|
||||
},
|
||||
performItemMove: (args) => {
|
||||
this._propertyStructureHelper.removeProperty(args.item.id!);
|
||||
let sortOrder = 0;
|
||||
if (this._propertyStructure.length > 0) {
|
||||
if (args.newIndex === 0) {
|
||||
sortOrder = (this._propertyStructure[0].sortOrder ?? 0) - 1;
|
||||
} else {
|
||||
sortOrder =
|
||||
(this._propertyStructure[Math.min(args.newIndex, this._propertyStructure.length - 1)].sortOrder ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
return this._propertyStructureHelper.insertProperty(args.item, sortOrder);
|
||||
},
|
||||
});
|
||||
|
||||
private _containerId: string | undefined;
|
||||
|
||||
@@ -28,6 +28,7 @@ export class UmbMediaTypeWorkspaceViewEditTabElement extends UmbLitElement {
|
||||
|
||||
config: UmbSorterConfig<PropertyTypeContainerModelBaseModel> = {
|
||||
...SORTER_CONFIG,
|
||||
// TODO: Missing handlers to work properly: performItemMove and performItemRemove
|
||||
performItemInsert: async (args) => {
|
||||
if (!this._groups) return false;
|
||||
const oldIndex = this._groups.findIndex((group) => group.id! === args.item.id);
|
||||
|
||||
@@ -38,6 +38,7 @@ export class UmbMediaTypeWorkspaceViewEditElement extends UmbLitElement implemen
|
||||
|
||||
config: UmbSorterConfig<PropertyTypeContainerModelBaseModel> = {
|
||||
...SORTER_CONFIG,
|
||||
// TODO: Missing handlers for these: performItemRemove, performItemMove
|
||||
performItemInsert: async (args) => {
|
||||
if (!this._tabs) return false;
|
||||
const oldIndex = this._tabs.findIndex((tab) => tab.id! === args.item.id);
|
||||
|
||||
@@ -60,6 +60,15 @@ export class UmbInputMediaElement extends FormControlMixin(UmbLitElement) {
|
||||
this.#pickerContext.setSelection(ids);
|
||||
}
|
||||
|
||||
@property({ type: String })
|
||||
filter?: string;
|
||||
|
||||
@property({ type: Boolean })
|
||||
showOpenButton?: boolean;
|
||||
|
||||
@property({ type: Boolean })
|
||||
ignoreUserStartNodes?: boolean;
|
||||
|
||||
@property()
|
||||
public set value(idsString: string) {
|
||||
// Its with full purpose we don't call super.value, as thats being handled by the observation of the context selection.
|
||||
@@ -73,6 +82,10 @@ export class UmbInputMediaElement extends FormControlMixin(UmbLitElement) {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
this.addValidator(
|
||||
'rangeUnderflow',
|
||||
@@ -90,18 +103,41 @@ export class UmbInputMediaElement extends FormControlMixin(UmbLitElement) {
|
||||
this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems));
|
||||
}
|
||||
|
||||
protected _openPicker() {
|
||||
// TODO: Configure the media picker, with `filter` and `ignoreUserStartNodes` [LK]
|
||||
console.log('_openPicker', [this.filter, this.ignoreUserStartNodes]);
|
||||
this.#pickerContext.openPicker({
|
||||
hideTreeRoot: true,
|
||||
});
|
||||
}
|
||||
|
||||
protected _openItem(item: MediaItemResponseModel) {
|
||||
// TODO: Implement the Content editing infinity editor. [LK]
|
||||
console.log('TODO: _openItem', item);
|
||||
}
|
||||
|
||||
protected getFormElement() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html` ${this._items?.map((item) => this.#renderItem(item))} ${this.#renderButton()} `;
|
||||
return html` ${this.#renderItems()} ${this.#renderButton()} `;
|
||||
}
|
||||
|
||||
#renderItems() {
|
||||
if (!this._items) return;
|
||||
// TODO: Add sorting. [LK]
|
||||
return html` ${this._items?.map((item) => this.#renderItem(item))} `;
|
||||
}
|
||||
|
||||
#renderButton() {
|
||||
if (this._items && this.max && this._items.length >= this.max) return;
|
||||
return html`
|
||||
<uui-button id="add-button" look="placeholder" @click=${() => this.#pickerContext.openPicker()} label=${this.localize.term('general_choose')}>
|
||||
<uui-button
|
||||
id="add-button"
|
||||
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>
|
||||
@@ -109,12 +145,14 @@ export class UmbInputMediaElement extends FormControlMixin(UmbLitElement) {
|
||||
}
|
||||
|
||||
#renderItem(item: MediaItemResponseModel) {
|
||||
// TODO: `file-ext` value has been hardcoded here. Find out if API model has value for it. [LK]
|
||||
// TODO: How to handle the `showOpenButton` option? [LK]
|
||||
return html`
|
||||
<uui-card-media
|
||||
name=${ifDefined(item.name === null ? undefined : item.name)}
|
||||
detail=${ifDefined(item.id)}
|
||||
file-ext="jpg">
|
||||
<!-- <uui-tag size="s" slot="tag" color="danger">Trashed</uui-tag> -->
|
||||
${this._renderIsTrashed(item)}
|
||||
<uui-action-bar slot="actions">
|
||||
<uui-button label="Copy media">
|
||||
<uui-icon name="icon-documents"></uui-icon>
|
||||
@@ -127,6 +165,11 @@ export class UmbInputMediaElement extends FormControlMixin(UmbLitElement) {
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderIsTrashed(item: MediaItemResponseModel) {
|
||||
if (!item.isTrashed) return;
|
||||
return html`<uui-tag size="s" slot="tag" color="danger">Trashed</uui-tag>`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
css`
|
||||
:host {
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { MemberTypeItemResponseModel } from '@umbraco-cms/backoffice/backen
|
||||
import { splitStringToArray } from '@umbraco-cms/backoffice/utils';
|
||||
|
||||
@customElement('umb-input-member-type')
|
||||
export class UmbMemberTypeInputElement extends FormControlMixin(UmbLitElement) {
|
||||
export class UmbInputMemberTypeElement extends FormControlMixin(UmbLitElement) {
|
||||
/**
|
||||
* This is a minimum amount of selected items in this input.
|
||||
* @type {number}
|
||||
@@ -94,32 +94,28 @@ export class UmbMemberTypeInputElement extends FormControlMixin(UmbLitElement) {
|
||||
this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems));
|
||||
}
|
||||
|
||||
protected _openPicker() {
|
||||
protected getFormElement() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
#openPicker() {
|
||||
this.#pickerContext.openPicker({
|
||||
hideTreeRoot: true,
|
||||
});
|
||||
}
|
||||
|
||||
protected getFormElement() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
${this.#renderItems()}
|
||||
${this.#renderAddButton()}
|
||||
`;
|
||||
return html` ${this.#renderItems()} ${this.#renderAddButton()} `;
|
||||
}
|
||||
|
||||
#renderItems() {
|
||||
if (!this._items) return;
|
||||
// TODO: Add sorting. [LK]
|
||||
return html`
|
||||
<uui-ref-list
|
||||
>${repeat(
|
||||
this._items,
|
||||
(item) => item.id,
|
||||
(item) => this._renderItem(item),
|
||||
(item) => this.#renderItem(item),
|
||||
)}</uui-ref-list
|
||||
>
|
||||
`;
|
||||
@@ -131,17 +127,18 @@ export class UmbMemberTypeInputElement extends FormControlMixin(UmbLitElement) {
|
||||
<uui-button
|
||||
id="add-button"
|
||||
look="placeholder"
|
||||
@click=${this._openPicker}
|
||||
@click=${this.#openPicker}
|
||||
label="${this.localize.term('general_choose')}"
|
||||
>${this.localize.term('general_choose')}</uui-button
|
||||
>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderItem(item: MemberTypeItemResponseModel) {
|
||||
#renderItem(item: MemberTypeItemResponseModel) {
|
||||
if (!item.id) return;
|
||||
return html`
|
||||
<uui-ref-node-document-type name=${ifDefined(item.name)}>
|
||||
${this.#renderIcon(item)}
|
||||
<uui-action-bar slot="actions">
|
||||
<uui-button
|
||||
@click=${() => this.#pickerContext.requestRemoveItem(item.id!)}
|
||||
@@ -153,6 +150,11 @@ export class UmbMemberTypeInputElement extends FormControlMixin(UmbLitElement) {
|
||||
`;
|
||||
}
|
||||
|
||||
#renderIcon(item: MemberTypeItemResponseModel) {
|
||||
if (!item.icon) return;
|
||||
return html`<uui-icon slot="icon" name=${item.icon}></uui-icon>`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
css`
|
||||
#add-button {
|
||||
@@ -162,10 +164,10 @@ export class UmbMemberTypeInputElement extends FormControlMixin(UmbLitElement) {
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbMemberTypeInputElement;
|
||||
export default UmbInputMemberTypeElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-input-member-type': UmbMemberTypeInputElement;
|
||||
'umb-input-member-type': UmbInputMemberTypeElement;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
import { RichTextRuleModelSortable, UmbStylesheetWorkspaceContext } from '../../stylesheet-workspace.context.js';
|
||||
import { UMB_MODAL_TEMPLATING_STYLESHEET_RTF_STYLE_SIDEBAR } from '../../manifests.js';
|
||||
import { StylesheetRichTextEditorStyleModalValue } from './stylesheet-workspace-view-rich-text-editor-style-sidebar.element.js';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import { UMB_MODAL_MANAGER_CONTEXT_TOKEN, UmbModalManagerContext, UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
import { RichTextRuleModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter';
|
||||
import { css, html, customElement, state, ifDefined, repeat } from '@umbraco-cms/backoffice/external/lit';
|
||||
|
||||
import './stylesheet-workspace-view-rich-text-editor-rule.element.js';
|
||||
|
||||
export const UMB_MODAL_TEMPLATING_STYLESHEET_RTF_STYLE_SIDEBAR_MODAL = new UmbModalToken<
|
||||
never,
|
||||
StylesheetRichTextEditorStyleModalValue
|
||||
>(UMB_MODAL_TEMPLATING_STYLESHEET_RTF_STYLE_SIDEBAR, {
|
||||
modal: {
|
||||
type: 'sidebar',
|
||||
size: 'medium',
|
||||
},
|
||||
value: { rule: null },
|
||||
});
|
||||
|
||||
const SORTER_CONFIG: UmbSorterConfig<RichTextRuleModel> = {
|
||||
compareElementToModel: (element: HTMLElement, model: RichTextRuleModel) => {
|
||||
return element.getAttribute('data-umb-rule-name') === model.name;
|
||||
},
|
||||
querySelectModelToElement: (container: HTMLElement, modelEntry: RichTextRuleModel) => {
|
||||
return container.querySelector('[data-umb-rule-name' + modelEntry.name + ']');
|
||||
},
|
||||
identifier: 'stylesheet-rules-sorter',
|
||||
itemSelector: 'umb-stylesheet-rich-text-editor-rule',
|
||||
containerSelector: '#rules-container',
|
||||
};
|
||||
|
||||
@customElement('umb-stylesheet-workspace-view-rich-text-editor')
|
||||
export class UmbStylesheetWorkspaceViewRichTextEditorElement extends UmbLitElement {
|
||||
@state()
|
||||
_rules: RichTextRuleModelSortable[] = [];
|
||||
|
||||
#context?: UmbStylesheetWorkspaceContext;
|
||||
private _modalContext?: UmbModalManagerContext;
|
||||
|
||||
#sorter = new UmbSorterController(this, {
|
||||
...SORTER_CONFIG,
|
||||
// TODO: Implement correctly, this code below was not correct:
|
||||
/*
|
||||
performItemInsert: ({ item, newIndex }) => {
|
||||
return this.#context?.findNewSortOrder(item, newIndex) ?? false;
|
||||
},
|
||||
performItemRemove: () => {
|
||||
//defined so the default does not run
|
||||
return true;
|
||||
},
|
||||
*/
|
||||
// End of todo comment.
|
||||
onChange: ({ model }) => {
|
||||
const oldValue = this._rules;
|
||||
this._rules = model;
|
||||
this.requestUpdate('_rules', oldValue);
|
||||
},
|
||||
});
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => {
|
||||
this.#context = workspaceContext as UmbStylesheetWorkspaceContext;
|
||||
|
||||
this.observe(this.#context.rules, (rules) => {
|
||||
this._rules = rules;
|
||||
this.#sorter.setModel(this._rules);
|
||||
});
|
||||
});
|
||||
|
||||
this.consumeContext(UMB_MODAL_MANAGER_CONTEXT_TOKEN, (instance) => {
|
||||
this._modalContext = instance;
|
||||
});
|
||||
}
|
||||
|
||||
openModal = (rule: RichTextRuleModelSortable | null = null) => {
|
||||
if (!this._modalContext) throw new Error('Modal context not found');
|
||||
const modal = this._modalContext.open(UMB_MODAL_TEMPLATING_STYLESHEET_RTF_STYLE_SIDEBAR_MODAL, {
|
||||
value: {
|
||||
rule,
|
||||
},
|
||||
});
|
||||
modal?.onSubmit().then((result) => {
|
||||
if (result.rule) {
|
||||
this.#context?.setRules([...this._rules, { ...result.rule, sortOrder: this._rules.length }]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
removeRule = (rule: RichTextRuleModelSortable) => {
|
||||
const rules = this._rules?.filter((r) => r.name !== rule.name);
|
||||
this.#context?.setRules(rules);
|
||||
};
|
||||
|
||||
render() {
|
||||
return html` <uui-box headline="Rich text editor styles">
|
||||
<div id="box-row">
|
||||
<p id="description">Define the styles that should be available in the rich text editor for this stylesheet.</p>
|
||||
<div id="rules">
|
||||
<div id="rules-container">
|
||||
${repeat(
|
||||
this._rules,
|
||||
(rule) => rule?.name ?? '' + rule?.sortOrder ?? '',
|
||||
(rule) =>
|
||||
html`<umb-stylesheet-rich-text-editor-rule
|
||||
.rule=${rule}
|
||||
data-umb-rule-name="${ifDefined(rule?.name)}"></umb-stylesheet-rich-text-editor-rule>`,
|
||||
)}
|
||||
</div>
|
||||
<uui-button label="Add rule" look="primary" @click=${() => this.openModal(null)}>Add</uui-button>
|
||||
</div>
|
||||
</div>
|
||||
</uui-box>`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#box-row {
|
||||
display: flex;
|
||||
gap: var(--uui-size-layout-1);
|
||||
}
|
||||
|
||||
#description {
|
||||
margin-top: 0;
|
||||
flex: 0 0 250px;
|
||||
}
|
||||
|
||||
#rules {
|
||||
flex: 1 1 auto;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
uui-box {
|
||||
margin: var(--uui-size-layout-1);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbStylesheetWorkspaceViewRichTextEditorElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-stylesheet-workspace-view-rich-text-editor': UmbStylesheetWorkspaceViewRichTextEditorElement;
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,6 @@
|
||||
"@umbraco-cms/backoffice/localization": ["src/packages/core/localization"],
|
||||
"@umbraco-cms/backoffice/macro": ["src/packages/core/macro"],
|
||||
"@umbraco-cms/backoffice/menu": ["src/packages/core/menu"],
|
||||
"@umbraco-cms/backoffice/meta": ["src/packages/core/meta"],
|
||||
"@umbraco-cms/backoffice/modal": ["src/packages/core/modal"],
|
||||
"@umbraco-cms/backoffice/notification": ["src/packages/core/notification"],
|
||||
"@umbraco-cms/backoffice/picker-input": ["src/packages/core/picker-input"],
|
||||
|
||||
@@ -74,7 +74,6 @@ export default {
|
||||
'@umbraco-cms/backoffice/localization': './src/packages/core/localization/index.ts',
|
||||
'@umbraco-cms/backoffice/macro': './src/packages/core/macro/index.ts',
|
||||
'@umbraco-cms/backoffice/menu': './src/packages/core/menu/index.ts',
|
||||
'@umbraco-cms/backoffice/meta': './src/packages/core/meta/index.ts',
|
||||
'@umbraco-cms/backoffice/modal': './src/packages/core/modal/index.ts',
|
||||
'@umbraco-cms/backoffice/notification': './src/packages/core/notification/index.ts',
|
||||
'@umbraco-cms/backoffice/picker-input': './src/packages/core/picker-input/index.ts',
|
||||
|
||||
Reference in New Issue
Block a user