first part of mega refactor for modal values

This commit is contained in:
Niels Lyngsø
2023-12-05 16:44:18 +01:00
parent d72b9ebd4d
commit dbe7f7cc5d
92 changed files with 626 additions and 482 deletions

View File

@@ -38,8 +38,10 @@ export class UmbInputListBaseElement extends UmbLitElement {
if (!this.pickerToken) return;
const modalContext = this._modalContext?.open(this.pickerToken, {
multiple: this.multiple,
selection: this.value,
data: {
multiple: this.multiple,
selection: this.value,
},
});
modalContext?.onSubmit().then((data: UmbPickerModalValue) => {

View File

@@ -179,38 +179,44 @@ export class UmbInputMarkdownElement extends FormControlMixin(UmbLitElement) {
private _insertLink() {
const selection = this.#editor?.getSelections()[0];
if (!selection) return;
if (!selection || !this._modalContext) return;
const selectedValue = this.#editor?.getValueInRange(selection);
this._focusEditor(); // Focus before opening modal
const modalContext = this._modalContext?.open(UMB_LINK_PICKER_MODAL, {
index: null,
link: { name: selectedValue },
config: { overlaySize: this.overlaySize },
const modalContext = this._modalContext.open(UMB_LINK_PICKER_MODAL, {
data: {
index: null,
config: { overlaySize: this.overlaySize },
},
value: {
link: { name: selectedValue },
},
});
modalContext
?.onSubmit()
.then((data) => {
.then((value) => {
if (!value) return;
const name = this.localize.term('general_name');
const url = this.localize.term('general_url');
this.#editor?.monacoEditor?.executeEdits('', [
{ range: selection, text: `[${data.link.name || name}](${data.link.url || url})` },
{ range: selection, text: `[${value.link.name || name}](${value.link.url || url})` },
]);
if (!data.link.name) {
if (!value.link.name) {
this.#editor?.select({
startColumn: selection.startColumn + 1,
endColumn: selection.startColumn + 1 + name.length,
endLineNumber: selection.startLineNumber,
startLineNumber: selection.startLineNumber,
});
} else if (!data.link.url) {
} else if (!value.link.url) {
this.#editor?.select({
startColumn: selection.startColumn + 3 + data.link.name.length,
endColumn: selection.startColumn + 3 + data.link.name.length + url.length,
startColumn: selection.startColumn + 3 + value.link.name.length,
endColumn: selection.startColumn + 3 + value.link.name.length + url.length,
endLineNumber: selection.startLineNumber,
startLineNumber: selection.startLineNumber,
});
@@ -227,12 +233,13 @@ export class UmbInputMarkdownElement extends FormControlMixin(UmbLitElement) {
const alt = this.#editor?.getValueInRange(selection) || 'alt text';
this._focusEditor(); // Focus before opening modal, otherwise cannot regain focus back after modal
const modalContext = this._modalContext?.open(UMB_MEDIA_TREE_PICKER_MODAL, {});
const modalContext = this._modalContext?.open(UMB_MEDIA_TREE_PICKER_MODAL);
modalContext
?.onSubmit()
.then((data) => {
const imgUrl = data.selection[0];
.then((value) => {
if (!value) return;
const imgUrl = value.selection[0];
this.#editor?.monacoEditor?.executeEdits('', [
//TODO: Get the correct media URL
{

View File

@@ -140,26 +140,30 @@ export class UmbInputMultiUrlElement extends FormControlMixin(UmbLitElement) {
}
return {
index: index,
link: {
name: data?.name,
published: data?.published,
queryString: data?.queryString,
target: data?.target,
trashed: data?.trashed,
udi: data?.udi,
url: data?.url,
data: {
index: index,
config: {
hideAnchor: this.hideAnchor,
ignoreUserStartNodes: this.ignoreUserStartNodes,
overlaySize: this.overlaySize || 'small',
},
},
config: {
hideAnchor: this.hideAnchor,
ignoreUserStartNodes: this.ignoreUserStartNodes,
overlaySize: this.overlaySize || 'small',
value: {
link: {
name: data?.name,
published: data?.published,
queryString: data?.queryString,
target: data?.target,
trashed: data?.trashed,
udi: data?.udi,
url: data?.url,
},
},
};
})
.onSubmit((submitData) => {
if (!submitData) return;
this._setSelection(submitData.link, submitData.index);
.onSubmit((value) => {
if (!value) return;
this._setSelection(value.link, this.myModalRegistration.modalContext?.data.index ?? null);
})
.observeRouteBuilder((routeBuilder) => {
this._modalRoute = routeBuilder;

View File

@@ -46,10 +46,12 @@ export class UmbInputMultipleTextStringItemElement extends FormControlMixin(UmbL
#onDelete() {
const modalContext = this._modalContext?.open(UMB_CONFIRM_MODAL, {
headline: `Delete ${this.value || 'item'}`,
content: 'Are you sure you want to delete this item?',
color: 'danger',
confirmLabel: 'Delete',
data: {
headline: `Delete ${this.value || 'item'}`,
content: 'Are you sure you want to delete this item?',
color: 'danger',
confirmLabel: 'Delete',
},
});
modalContext?.onSubmit().then(() => {

View File

@@ -49,19 +49,21 @@ export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) {
super();
this.#editDataTypeModal = new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL).onSetup(() => {
return { entityType: 'data-type', preset: {} };
return { data: { entityType: 'data-type', preset: {} } };
});
new UmbModalRouteRegistrationController(this, UMB_DATA_TYPE_PICKER_FLOW_MODAL)
.onSetup(() => {
return {
selection: this._ids,
submitLabel: 'Submit',
data: {
submitLabel: 'Submit',
},
value: { selection: this._ids ?? [] },
};
})
.onSubmit((submitData) => {
// TODO: we might should set the alias to null or empty string, if no selection.
this.value = submitData.selection.join(',');
// TODO: we maybe should set the alias to null, if no selection?
this.value = submitData?.selection.join(',') ?? '';
this.dispatchEvent(new CustomEvent('change', { composed: true, bubbles: true }));
})
.observeRouteBuilder((routeBuilder) => {

View File

@@ -24,7 +24,8 @@ export class UmbCopyDataTypeEntityAction extends UmbEntityActionBase<UmbCopyData
if (!this.repository) throw new Error('Repository is not available');
const modalContext = this.#modalManagerContext?.open(UMB_DATA_TYPE_PICKER_MODAL);
const { selection } = await modalContext.onSubmit();
await this.repository.copy(this.unique, selection[0]);
const value = await modalContext.onSubmit();
if (!value) return;
await this.repository.copy(this.unique, value.selection[0]);
}
}

View File

@@ -20,7 +20,9 @@ export class UmbCreateDataTypeEntityAction extends UmbEntityActionBase<UmbDataTy
if (!this.repository) throw new Error('Repository is not available');
this.#modalManagerContext?.open(UMB_DATA_TYPE_CREATE_OPTIONS_MODAL, {
parentKey: this.unique,
data: {
parentKey: this.unique,
},
});
}
}

View File

@@ -30,7 +30,9 @@ export class UmbDataTypeCreateOptionsModalElement extends UmbLitElement {
#onClick(event: PointerEvent) {
event.stopPropagation();
const folderModalHandler = this.#modalContext?.open(UMB_FOLDER_MODAL, {
repositoryAlias: UMB_DATA_TYPE_FOLDER_REPOSITORY_ALIAS,
data: {
repositoryAlias: UMB_DATA_TYPE_FOLDER_REPOSITORY_ALIAS,
},
});
folderModalHandler?.onSubmit().then(() => this.modalContext?.submit());
}

View File

@@ -6,15 +6,15 @@ import {
UmbModalContext,
UmbDataTypePickerFlowDataTypePickerModalData,
UmbDataTypePickerFlowDataTypePickerModalValue,
UmbModalBaseElement,
} from '@umbraco-cms/backoffice/modal';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { FolderTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
@customElement('umb-data-type-picker-flow-data-type-picker-modal')
export class UmbDataTypePickerFlowDataTypePickerModalElement extends UmbLitElement {
@property({ type: Object })
data?: UmbDataTypePickerFlowDataTypePickerModalData;
export class UmbDataTypePickerFlowDataTypePickerModalElement extends UmbModalBaseElement<
UmbDataTypePickerFlowDataTypePickerModalData,
UmbDataTypePickerFlowDataTypePickerModalValue
> {
@state()
private _dataTypes?: Array<FolderTreeItemResponseModel>;
@@ -61,12 +61,14 @@ export class UmbDataTypePickerFlowDataTypePickerModalElement extends UmbLitEleme
private _handleClick(dataType: FolderTreeItemResponseModel) {
if (dataType.id) {
this.modalContext?.submit({ dataTypeId: dataType.id });
this._value = { dataTypeId: dataType.id };
this.modalContext?.submit();
}
}
private _handleCreate() {
this.modalContext?.submit({ createNewWithPropertyEditorUiAlias: this._propertyEditorUiAlias });
this._value = { createNewWithPropertyEditorUiAlias: this._propertyEditorUiAlias };
this.modalContext?.submit();
}
private _close() {

View File

@@ -7,32 +7,25 @@ import {
UMB_WORKSPACE_MODAL,
UmbDataTypePickerFlowModalData,
UmbDataTypePickerFlowModalValue,
UmbModalContext,
UmbModalBaseElement,
UmbModalRouteBuilder,
UmbModalRouteRegistrationController,
} from '@umbraco-cms/backoffice/modal';
import { ManifestPropertyEditorUi, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import type { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
interface GroupedItems<T> {
[key: string]: Array<T>;
}
@customElement('umb-data-type-picker-flow-modal')
export class UmbDataTypePickerFlowModalElement extends UmbLitElement {
@property({ attribute: false })
modalContext?: UmbModalContext<UmbDataTypePickerFlowModalData, UmbDataTypePickerFlowModalValue>;
@property({ type: Object })
public get data(): UmbDataTypePickerFlowModalData | undefined {
return this._data;
}
public set data(value: UmbDataTypePickerFlowModalData | undefined) {
this._data = value;
this._selection = this.data?.selection ?? [];
export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement<
UmbDataTypePickerFlowModalData,
UmbDataTypePickerFlowModalValue
> {
public set data(value: UmbDataTypePickerFlowModalData) {
super.data = value;
this._submitLabel = this.data?.submitLabel ?? this._submitLabel;
}
private _data?: UmbDataTypePickerFlowModalData | undefined;
@state()
private _groupedDataTypes?: GroupedItems<EntityTreeItemResponseModel>;
@@ -64,14 +57,17 @@ export class UmbDataTypePickerFlowModalElement extends UmbLitElement {
.addAdditionalPath(':uiAlias')
.onSetup((routingInfo) => {
return {
propertyEditorUiAlias: routingInfo.uiAlias,
data: {
propertyEditorUiAlias: routingInfo.uiAlias,
},
value: undefined,
};
})
.onSubmit((submitData) => {
if (submitData.dataTypeId) {
if (submitData?.dataTypeId) {
this._select(submitData.dataTypeId);
this._submit();
} else if (submitData.createNewWithPropertyEditorUiAlias) {
this._submitModal();
} else if (submitData?.createNewWithPropertyEditorUiAlias) {
this._createDataType(submitData.createNewWithPropertyEditorUiAlias);
}
})
@@ -83,11 +79,11 @@ export class UmbDataTypePickerFlowModalElement extends UmbLitElement {
this._createDataTypeModal = new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL)
.addAdditionalPath(':uiAlias')
.onSetup((params) => {
return { entityType: 'data-type', preset: { propertyEditorUiAlias: params.uiAlias } };
return { data: { entityType: 'data-type', preset: { propertyEditorUiAlias: params.uiAlias } } };
})
.onSubmit((submitData) => {
this._select(submitData.id);
this._submit();
.onSubmit((value) => {
this._select(value?.id);
this._submitModal();
});
this.#init();
@@ -120,15 +116,25 @@ export class UmbDataTypePickerFlowModalElement extends UmbLitElement {
});
}
private _handleDataTypeClick(dataType: EntityTreeItemResponseModel) {
if (dataType.id) {
this._select(dataType.id);
this._submit();
connectedCallback(): void {
super.connectedCallback();
if (this.modalContext) {
this.observe(this.modalContext.value, (value) => {
this._selection = value?.selection ?? [];
});
}
}
private _select(id: string) {
this._selection = [id];
private _handleDataTypeClick(dataType: EntityTreeItemResponseModel) {
if (dataType.id) {
this._select(dataType.id);
this._submitModal();
}
}
private _select(id: string | undefined) {
this._selection = id ? [id] : [];
}
private _handleFilterInput(event: UUIInputEvent) {
@@ -142,8 +148,8 @@ export class UmbDataTypePickerFlowModalElement extends UmbLitElement {
(dataType) => dataType.name?.toLowerCase().includes(this.#currentFilterQuery),
);
/* TODO: data type items doesn't have a group property. We will need a reference to the property editor UI to get the group.
this is a temp solution to group them as uncategorized. The same result as with the lodash groupBy.
/* TODO: data type items doesn't have a group property. We will need a reference to the Property Editor UI to get the group.
this is a temp solution to group them as uncategorized. The same result as with the lodash groupBy.
*/
this._groupedDataTypes = {
undefined: filteredDataTypes,
@@ -170,20 +176,12 @@ export class UmbDataTypePickerFlowModalElement extends UmbLitElement {
);
}
private _close() {
this.modalContext?.reject();
}
private _submit() {
this.modalContext?.submit({ selection: this._selection });
}
render() {
return html`
<umb-body-layout headline="Select editor" class="uui-text">
<uui-box> ${this._renderFilter()} ${this._renderGrid()} </uui-box>
<div slot="actions">
<uui-button label="Close" @click=${this._close}></uui-button>
<uui-button label="Close" @click=${this._rejectModal}></uui-button>
</div>
</umb-body-layout>
`;

View File

@@ -31,7 +31,7 @@ export class UmbPropertyEditorUIPickerModalElement extends UmbModalBaseElement<
connectedCallback(): void {
super.connectedCallback();
this._selection = this.data?.selection ?? [];
this._selection = this._value?.selection ?? [];
this._submitLabel = this.data?.submitLabel ?? this._submitLabel;
this._usePropertyEditorUIs();

View File

@@ -1,7 +1,7 @@
import { Meta, Story } from '@storybook/web-components';
import type { UmbPropertyEditorUIPickerModalElement } from './property-editor-ui-picker-modal.element.js';
import { html } from '@umbraco-cms/backoffice/external/lit';
import type { UmbPropertyEditorUIPickerModalData } from '@umbraco-cms/backoffice/modal';
import type { UmbPropertyEditorUIPickerModalValue } from '@umbraco-cms/backoffice/modal';
import './property-editor-ui-picker-modal.element.js';
import '../../../components/body-layout/body-layout.element.js';
@@ -12,8 +12,8 @@ export default {
id: 'umb-property-editor-ui-picker-modal',
} as Meta;
const data: UmbPropertyEditorUIPickerModalData = { selection: [] };
const data: UmbPropertyEditorUIPickerModalValue = { selection: [] };
export const Overview: Story<UmbPropertyEditorUIPickerModalElement> = () => html`
<umb-property-editor-ui-picker-modal .data=${data as any}></umb-property-editor-ui-picker-modal>
<umb-property-editor-ui-picker-modal .value=${data as any}></umb-property-editor-ui-picker-modal>
`;

View File

@@ -71,11 +71,13 @@ export class UmbDataTypeDetailsWorkspaceViewEditElement extends UmbLitElement im
private _openPropertyEditorUIPicker() {
const modalContext = this._modalContext?.open(UMB_PROPERTY_EDITOR_UI_PICKER_MODAL, {
selection: this._propertyEditorUiAlias ? [this._propertyEditorUiAlias] : [],
value: {
selection: this._propertyEditorUiAlias ? [this._propertyEditorUiAlias] : [],
},
});
modalContext?.onSubmit().then(({ selection }) => {
this._workspaceContext?.setPropertyEditorUiAlias(selection[0]);
modalContext?.onSubmit().then((value) => {
this._workspaceContext?.setPropertyEditorUiAlias(value?.selection[0]);
});
}

View File

@@ -78,7 +78,9 @@ export class UmbDebugElement extends UmbLitElement {
private _openDialog() {
this._modalContext?.open(UMB_CONTEXT_DEBUGGER_MODAL, {
content: html`${this._renderContextAliases()}`,
data: {
content: html`${this._renderContextAliases()}`,
},
});
}

View File

@@ -23,8 +23,10 @@ export class UmbCreateFolderEntityAction<T extends UmbFolderRepository> extends
if (!this.repository || !this.#modalContext) return;
const modalContext = this.#modalContext.open(UMB_FOLDER_MODAL, {
repositoryAlias: this.repositoryAlias,
parentUnique: this.unique ?? null,
data: {
repositoryAlias: this.repositoryAlias,
parentUnique: this.unique ?? null,
},
});
await modalContext.onSubmit();

View File

@@ -27,10 +27,12 @@ export class UmbDeleteFolderEntityAction<T extends UmbFolderRepository> extends
if (folder) {
// TODO: maybe we can show something about how many items are part of the folder?
const modalContext = this.#modalContext.open(UMB_CONFIRM_MODAL, {
headline: `Delete folder ${folder.name}`,
content: 'Are you sure you want to delete this folder?',
color: 'danger',
confirmLabel: 'Delete',
data: {
headline: `Delete folder ${folder.name}`,
content: 'Are you sure you want to delete this folder?',
color: 'danger',
confirmLabel: 'Delete',
},
});
await modalContext.onSubmit();

View File

@@ -28,10 +28,12 @@ export class UmbDeleteEntityAction<
//const { data } = await this.repository.requestItems([this.unique]);
const modalContext = this.#modalManager.open(UMB_CONFIRM_MODAL, {
headline: `Delete`,
content: 'Are you sure you want to delete this item?',
color: 'danger',
confirmLabel: 'Delete',
data: {
headline: `Delete`,
content: 'Are you sure you want to delete this item?',
color: 'danger',
confirmLabel: 'Delete',
},
});
await modalContext.onSubmit();

View File

@@ -25,8 +25,10 @@ export class UmbFolderUpdateEntityAction<
if (!this.repository || !this.#modalContext) return;
const modalContext = this.#modalContext.open(UMB_FOLDER_MODAL, {
repositoryAlias: this.repositoryAlias,
unique: this.unique,
data: {
repositoryAlias: this.repositoryAlias,
unique: this.unique,
},
});
await modalContext.onSubmit();

View File

@@ -30,10 +30,12 @@ export class UmbTrashEntityAction<
const item = data[0];
const modalContext = this.#modalContext?.open(UMB_CONFIRM_MODAL, {
headline: `Trash ${item.name}`,
content: 'Are you sure you want to move this item to the recycle bin?',
color: 'danger',
confirmLabel: 'Trash',
data: {
headline: `Trash ${item.name}`,
content: 'Are you sure you want to move this item to the recycle bin?',
color: 'danger',
confirmLabel: 'Trash',
},
});
modalContext?.onSubmit().then(() => {

View File

@@ -17,7 +17,8 @@ export class UmbCodeEditorModalElement extends UmbModalBaseElement<UmbCodeEditor
}
#handleConfirm() {
this.modalContext?.submit({ content: this._codeEditor?.editor?.monacoEditor?.getValue() ?? '' });
this._value = { content: this._codeEditor?.editor?.monacoEditor?.getValue() ?? '' };
this.modalContext?.submit();
}
#handleCancel() {

View File

@@ -29,13 +29,14 @@ export class UmbEmbeddedMediaModalElement extends UmbModalBaseElement<
#embedResult!: OEmbedResult;
#handleConfirm() {
this.modalContext?.submit({
this._value = {
preview: this.#embedResult.markup,
originalWidth: this._model.width,
originalHeight: this._model.originalHeight,
width: this.#embedResult.width,
height: this.#embedResult.height,
});
};
this.modalContext?.submit();
}
#handleCancel() {

View File

@@ -48,15 +48,6 @@ export class UmbIconPickerModalElement extends UmbModalBaseElement<UmbIconPicker
}
}
#close() {
this.modalContext?.reject();
}
#submit() {
// TODO: Shouldnt we stop sending the value here, instead let the modal context send of its value. and then its up to any one using it to make sure Modal Context value is up to date.
this.modalContext?.submit(this._modalValue);
}
#onColorChange(e: UUIColorSwatchesEvent) {
this.modalContext?.updateValue({ icon: e.target.value });
}
@@ -104,8 +95,8 @@ export class UmbIconPickerModalElement extends UmbModalBaseElement<UmbIconPicker
<hr />
<uui-scroll-container id="icon-selection">${this.renderIconSelection()}</uui-scroll-container>
</div>
<uui-button slot="actions" label="close" @click="${this.#close}">Close</uui-button>
<uui-button slot="actions" color="positive" look="primary" @click="${this.#submit}" label="Submit">
<uui-button slot="actions" label="close" @click="${this._rejectModal}">Close</uui-button>
<uui-button slot="actions" color="positive" look="primary" @click="${this._submitModal}" label="Submit">
Submit
</uui-button>
</umb-body-layout>

View File

@@ -5,7 +5,7 @@ import { Meta, Story } from '@storybook/web-components';
import type { UmbIconPickerModalElement } from './icon-picker-modal.element.js';
import { html } from '@umbraco-cms/backoffice/external/lit';
import { UmbIconPickerModalData, UmbIconPickerModalValue } from '@umbraco-cms/backoffice/modal';
import { UmbIconPickerModalValue } from '@umbraco-cms/backoffice/modal';
export default {
title: 'API/Modals/Layouts/Icon Picker',
@@ -13,8 +13,6 @@ export default {
id: 'umb-icon-picker-modal',
} as Meta;
const data: UmbIconPickerModalData = {};
const value: UmbIconPickerModalValue = {
color: undefined,
icon: undefined,
@@ -24,5 +22,5 @@ export const Overview: Story<UmbIconPickerModalElement> = () => html`
<!-- TODO: figure out if generics are allowed for properties:
https://github.com/runem/lit-analyzer/issues/149
https://github.com/runem/lit-analyzer/issues/163 -->
<umb-icon-picker-modal .data=${data as any} .value=${value}></umb-icon-picker-modal>
<umb-icon-picker-modal .value=${value}></umb-icon-picker-modal>
`;

View File

@@ -17,19 +17,7 @@ export class UmbLinkPickerModalElement extends UmbModalBaseElement<UmbLinkPicker
_selectedKey?: string;
@state()
_index: number | null = null;
@state()
_link: UmbLinkPickerLink = {
icon: null,
name: null,
published: true,
queryString: null,
target: null,
trashed: false,
udi: null,
url: null,
};
_link: UmbLinkPickerLink = {};
@state()
_layout: UmbLinkPickerConfig = {
@@ -52,15 +40,21 @@ export class UmbLinkPickerModalElement extends UmbModalBaseElement<UmbLinkPicker
@query('#link-title-input')
private _linkTitleInput!: UUIInputElement;
constructor() {
super();
}
connectedCallback() {
super.connectedCallback();
if (!this.data) return;
this._index = this.data?.index;
this._link = this.data?.link;
this._layout = this.data?.config;
if (!this._link.udi) return;
this._selectedKey = getKeyFromUdi(this._link.udi);
if (this.modalContext) {
this.observe(this.modalContext.value, (value) => {
this._link = value?.link ?? {};
this._selectedKey = this._link?.udi ? getKeyFromUdi(this._link.udi) : undefined;
});
}
this._layout = this.data?.config;
}
private _handleQueryString() {
@@ -69,13 +63,16 @@ export class UmbLinkPickerModalElement extends UmbModalBaseElement<UmbLinkPicker
if (query.startsWith('#') || query.startsWith('?')) {
this._link.queryString = query;
this.modalContext?.updateValue({ link: this._link });
return;
}
if (query.includes('=')) {
this._link.queryString = `?${query}`;
this.modalContext?.updateValue({ link: this._link });
} else {
this._link.queryString = `#${query}`;
this.modalContext?.updateValue({ link: this._link });
}
}
@@ -88,6 +85,7 @@ export class UmbLinkPickerModalElement extends UmbModalBaseElement<UmbLinkPicker
if (!selectedKey) {
this._link.url = '';
this._link.udi = undefined;
this.modalContext?.updateValue({ link: this._link });
this._selectedKey = undefined;
this.requestUpdate();
return;
@@ -98,11 +96,12 @@ export class UmbLinkPickerModalElement extends UmbModalBaseElement<UmbLinkPicker
this._selectedKey = selectedKey;
this._link.udi = udi;
this._link.url = udi;
this.modalContext?.updateValue({ link: this._link });
this.requestUpdate();
}
private _submit() {
this.modalContext?.submit({ index: this._index, link: this._link });
this.modalContext?.submit();
}
private _close() {

View File

@@ -46,8 +46,7 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
@state()
protected _ownerDocumentType?: UmbPropertySettingsModalValue;
@state()
protected _returnData!: UmbPropertySettingsModalValue;
protected _originalPropertyData!: UmbPropertySettingsModalValue;
connectedCallback(): void {
super.connectedCallback();
@@ -64,20 +63,22 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
);
});
this._returnData = JSON.parse(JSON.stringify(this.data?.propertyData ?? {}));
this._originalPropertyData = this._value;
// TODO: Use some utility method for this deep clone:
this._value = JSON.parse(JSON.stringify(this._value ?? {}));
this._returnData.validation ??= {};
this._value.validation ??= {};
const regEx = this._returnData.validation.regEx ?? null;
const regEx = this._value.validation.regEx ?? null;
const newlySelected = this._customValidationOptions.find((option) => {
option.selected = option.value === regEx;
return option.selected;
});
if (newlySelected === undefined) {
this._customValidationOptions[4].selected = true;
this._returnData.validation.regEx = this._customValidationOptions[4].value;
this._value.validation.regEx = this._customValidationOptions[4].value;
} else {
this._returnData.validation.regEx = regEx;
this._value.validation.regEx = regEx;
}
}
@@ -99,18 +100,18 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
const isValid = form.checkValidity();
if (!isValid) return;
this.modalContext?.submit(this._returnData);
this.modalContext?.submit();
}
#onNameChange(event: UUIInputEvent) {
const oldName = this._returnData.name;
const oldAlias = this._returnData.alias;
this._returnData.name = event.target.value.toString();
const oldName = this._value.name;
const oldAlias = this._value.alias;
this._value.name = event.target.value.toString();
if (this._aliasLocked) {
const expectedOldAlias = generateAlias(oldName ?? '');
// Only update the alias if the alias matches a generated alias of the old name (otherwise the alias is considered one written by the user.)
if (expectedOldAlias === oldAlias) {
this._returnData.alias = generateAlias(this._returnData.name);
this._value.alias = generateAlias(this._value.name);
this.requestUpdate('_returnData');
}
}
@@ -119,43 +120,43 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
#onAliasChange(event: UUIInputEvent) {
const alias = generateAlias(event.target.value.toString());
if (!this._aliasLocked) {
this._returnData.alias = alias;
this._value.alias = alias;
} else {
this._returnData.alias = this.data?.propertyData?.alias;
this._value.alias = this._originalPropertyData.alias;
}
this.requestUpdate('_returnData');
}
#onDataTypeIdChange(event: UUIInputEvent) {
const dataTypeId = event.target.value.toString();
this._returnData.dataTypeId = dataTypeId;
this._value.dataTypeId = dataTypeId;
this.requestUpdate('_returnData');
}
#onMandatoryChange(event: UUIBooleanInputEvent) {
const value = event.target.checked;
this._returnData.validation!.mandatory = value;
this._value.validation!.mandatory = value;
this.requestUpdate('_returnData');
}
#onMandatoryMessageChange(event: UUIInputEvent) {
const value = event.target.value.toString();
this._returnData.validation!.mandatoryMessage = value;
this._value.validation!.mandatoryMessage = value;
this.requestUpdate('_returnData');
}
#setAppearanceNormal() {
const currentValue = this._returnData.appearance?.labelOnTop;
const currentValue = this._value.appearance?.labelOnTop;
if (currentValue !== true) return;
this._returnData.appearance!.labelOnTop = false;
this._value.appearance!.labelOnTop = false;
this.requestUpdate('_returnData');
}
#setAppearanceTop() {
const currentValue = this._returnData.appearance?.labelOnTop;
const currentValue = this._value.appearance?.labelOnTop;
if (currentValue === true) return;
this._returnData.appearance!.labelOnTop = true;
this._value.appearance!.labelOnTop = true;
this.requestUpdate('_returnData');
}
@@ -170,7 +171,7 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
option.selected = option.value === regEx;
});
this._returnData.validation!.regEx = regEx ?? null;
this._value.validation!.regEx = regEx ?? null;
this.requestUpdate('_returnData');
this.requestUpdate('_customValidationOptions');
}
@@ -184,17 +185,17 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
if (betterChoice === undefined) {
this._customValidationOptions[4].selected = true;
}
this._returnData.validation!.regEx = regEx;
this._value.validation!.regEx = regEx;
this.requestUpdate('_returnData');
this.requestUpdate('_customValidationOptions');
}
#onValidationMessageChange(event: UUIInputEvent) {
this._returnData.validation!.regExMessage = event.target.value.toString();
this._value.validation!.regExMessage = event.target.value.toString();
this.requestUpdate('_returnData');
}
#onVaryByCultureChange(event: UUIBooleanInputEvent) {
this._returnData.variesByCulture = event.target.checked;
this._value.variesByCulture = event.target.checked;
this.requestUpdate('_returnData');
}
@@ -214,7 +215,7 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
id="name-input"
name="name"
@input=${this.#onNameChange}
.value=${this._returnData.name}
.value=${this._value.name}
placeholder="Enter a name...">
<!-- TODO: validation for bad characters -->
</uui-input>
@@ -222,7 +223,7 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
id="alias-input"
name="alias"
@input=${this.#onAliasChange}
.value=${this._returnData.alias}
.value=${this._value.alias}
placeholder="Enter alias..."
?disabled=${this._aliasLocked}>
<!-- TODO: validation for bad characters -->
@@ -234,10 +235,10 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
id="description-input"
name="description"
placeholder="Enter description..."
.value=${this._returnData.description}></uui-textarea>
.value=${this._value.description}></uui-textarea>
</div>
<umb-data-type-flow-input
.value=${this._returnData.dataTypeId ?? ''}
.value=${this._value.dataTypeId ?? ''}
@change=${this.#onDataTypeIdChange}></umb-data-type-flow-input>
<hr />
<div class="container">
@@ -267,7 +268,7 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
return html`<button
type="button"
@click=${this.#setAppearanceNormal}
class="appearance left ${this._returnData.appearance?.labelOnTop ? '' : 'selected'}">
class="appearance left ${this._value.appearance?.labelOnTop ? '' : 'selected'}">
<svg width="200" height="48" viewBox="0 0 200 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="94" height="14" rx="6" fill="currentColor" />
<rect y="22" width="64" height="9" rx="4" fill="currentColor" fill-opacity="0.4" />
@@ -282,7 +283,7 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
<button
type="button"
@click=${this.#setAppearanceTop}
class="appearance top ${this._returnData.appearance?.labelOnTop ? 'selected' : ''}">
class="appearance top ${this._value.appearance?.labelOnTop ? 'selected' : ''}">
<svg width="140" height="48" viewBox="0 0 140 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="90" height="14" rx="6" fill="currentColor" />
<rect y="22" width="64" height="9" rx="4" fill="currentColor" fill-opacity="0.4" />
@@ -299,13 +300,13 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
<uui-toggle
@change=${this.#onMandatoryChange}
id="mandatory"
.checked=${this._returnData.validation?.mandatory ?? false}
.checked=${this._value.validation?.mandatory ?? false}
slot="editor"></uui-toggle>
</div>
${this._returnData.validation?.mandatory
${this._value.validation?.mandatory
? html`<uui-input
name="mandatory-message"
value=${this._returnData.validation?.mandatoryMessage ?? ''}
value=${this._value.validation?.mandatoryMessage ?? ''}
@change=${this.#onMandatoryMessageChange}
style="margin-top: var(--uui-size-space-1)"
id="mandatory-message"
@@ -319,17 +320,17 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
@change=${this.#onCustomValidationChange}
.options=${this._customValidationOptions}></uui-select>
${this._returnData.validation?.regEx !== null
${this._value.validation?.regEx !== null
? html`
<uui-input
name="pattern"
style="margin-bottom: var(--uui-size-space-1); margin-top: var(--uui-size-space-5);"
@change=${this.#onValidationRegExChange}
.value=${this._returnData.validation?.regEx ?? ''}></uui-input>
.value=${this._value.validation?.regEx ?? ''}></uui-input>
<uui-textarea
name="pattern-message"
@change=${this.#onValidationMessageChange}
.value=${this._returnData.validation?.regExMessage ?? ''}></uui-textarea>
.value=${this._value.validation?.regExMessage ?? ''}></uui-textarea>
`
: nothing} `;
}
@@ -346,7 +347,7 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
#renderVaryByCulture() {
return html`<uui-toggle
@change=${this.#onVaryByCultureChange}
.checked=${this._returnData.variesByCulture ?? false}
.checked=${this._value.variesByCulture ?? false}
label="Vary by culture"></uui-toggle> `;
}

View File

@@ -19,9 +19,10 @@ export class UmbSectionPickerModalElement extends UmbModalBaseElement<
#selectionManager = new UmbSelectionManager();
#submit() {
this.modalContext?.submit({
this._value = {
selection: this.#selectionManager.getSelection(),
});
};
this.modalContext?.submit();
}
#close() {

View File

@@ -51,7 +51,9 @@ export class UmbTemplateModalElement extends UmbModalBaseElement<UmbTemplateModa
if (!this._template?.id) return;
this.#saveTemplate();
this.modalContext?.submit({ id: this._template.id });
this._value = { id: this._template.id };
this.modalContext?.submit();
}
private _close() {

View File

@@ -19,25 +19,23 @@ export class UmbTreePickerModalElement<TreeItemType extends TreeItemPresentation
connectedCallback() {
super.connectedCallback();
this._selection = this.data?.selection ?? [];
// TODO: We should make a nicer way to observe the value..
if (this.modalContext) {
this.observe(this.modalContext.value, (value) => {
this._selection = value?.selection ?? [];
});
}
this._multiple = this.data?.multiple ?? false;
}
#onSelectionChange(e: CustomEvent) {
e.stopPropagation();
const element = e.target as UmbTreeElement;
this._selection = element.selection;
this._value = { selection: element.selection };
this.dispatchEvent(new UmbSelectionChangeEvent());
}
#submit() {
this.modalContext?.submit({ selection: this._selection });
}
#close() {
this.modalContext?.reject();
}
render() {
return html`
<umb-body-layout headline="Select">
@@ -52,8 +50,8 @@ export class UmbTreePickerModalElement<TreeItemType extends TreeItemPresentation
?multiple=${this._multiple}></umb-tree>
</uui-box>
<div slot="actions">
<uui-button label="Close" @click=${this.#close}></uui-button>
<uui-button label="Submit" look="primary" color="positive" @click=${this.#submit}></uui-button>
<uui-button label="Close" @click=${this._rejectModal}></uui-button>
<uui-button label="Submit" look="primary" color="positive" @click=${this._submitModal}></uui-button>
</div>
</umb-body-layout>
`;

View File

@@ -18,7 +18,13 @@ export abstract class UmbModalBaseElement<
public modalContext?: UmbModalContext<ModalDataType, ModalValueType>;
@property({ type: Object, attribute: false })
public data?: ModalDataType;
public get data(): ModalDataType | undefined {
return this._data;
}
public set data(value: ModalDataType | undefined) {
this._data = value;
}
private _data?: ModalDataType | undefined;
@state()
public get _value(): ModalValueType {
@@ -34,7 +40,7 @@ export abstract class UmbModalBaseElement<
* @memberof UmbModalBaseElement
*/
protected _submitModal() {
this.modalContext?.submit(this._value);
this.modalContext?.submit();
}
/**

View File

@@ -1,6 +1,5 @@
import type { UmbModalToken } from './token/modal-token.js';
import { UmbModalContext, UmbModalContextClass } from './index.js';
import type { IRouterSlot } from '@umbraco-cms/backoffice/external/router-slot';
import { UmbModalContext, type UmbModalContextClassArgs } from './index.js';
import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
import { BehaviorSubject } from '@umbraco-cms/backoffice/external/rxjs';
import { appendToFrozenArray } from '@umbraco-cms/backoffice/observable-api';
@@ -29,22 +28,19 @@ export class UmbModalManagerContext {
* Opens a modal or sidebar modal
* @public
* @param {(string | UmbModalToken)} modalAlias
* @param {ModalData} data
* @param {UmbModalConfig} config
* @param {IRouterSlot | null} router
* @param {UmbModalContextClassArgs} args
* @return {*} {UmbModalHandler}
* @memberof UmbModalManagerContext
*/
public open<ModalData extends object = object, ModalValue = unknown>(
modalAlias: string | UmbModalToken<ModalData, ModalValue>,
data?: ModalData,
config?: UmbModalConfig,
router: IRouterSlot | null = null,
public open<
ModalData extends object = object,
ModalValue = unknown,
ModalAliasTypeAsToken extends UmbModalToken = UmbModalToken<ModalData, ModalValue>,
>(
modalAlias: UmbModalToken<ModalData, ModalValue> | string,
args: UmbModalContextClassArgs<ModalAliasTypeAsToken> = {},
) {
const modalContext = new UmbModalContextClass(router, modalAlias, data, config) as unknown as UmbModalContext<
ModalData,
ModalValue
>;
const modalContext = new UmbModalContext(modalAlias, args);
this.#modals.next(
appendToFrozenArray(this.#modals.getValue(), modalContext, (entry) => entry.key === modalContext.key),

View File

@@ -8,23 +8,38 @@ import type { Params } from '@umbraco-cms/backoffice/router';
export type UmbModalRouteBuilder = (params: { [key: string]: string | number } | null) => string;
export class UmbModalRouteRegistration<UmbModalTokenData extends object = object, UmbModalTokenResult = any> {
export type UmbModalRouteSetupReturn<UmbModalTokenData, UmbModalTokenValue> = UmbModalTokenValue extends undefined
? {
data: UmbModalTokenData;
value?: UmbModalTokenValue;
}
: {
data: UmbModalTokenData;
value: UmbModalTokenValue;
};
export class UmbModalRouteRegistration<UmbModalTokenData extends object = object, UmbModalTokenValue = any> {
#key: string;
#path: string | null;
#modalAlias: UmbModalToken<UmbModalTokenData, UmbModalTokenResult> | string;
#modalAlias: UmbModalToken<UmbModalTokenData, UmbModalTokenValue> | string;
#modalConfig?: UmbModalConfig;
#onSetupCallback?: (routingInfo: Params) => Promise<UmbModalTokenData | false> | UmbModalTokenData | false;
#onSubmitCallback?: (data: UmbModalTokenResult) => void;
#onSetupCallback?: (
routingInfo: Params,
) =>
| Promise<UmbModalRouteSetupReturn<UmbModalTokenData, UmbModalTokenValue> | false>
| UmbModalRouteSetupReturn<UmbModalTokenData, UmbModalTokenValue>
| false;
#onSubmitCallback?: (data: UmbModalTokenValue) => void;
#onRejectCallback?: () => void;
#modalManagerContext: UmbModalContext<UmbModalTokenData, UmbModalTokenResult> | undefined;
#modalManagerContext: UmbModalContext<UmbModalTokenData, UmbModalTokenValue> | undefined;
#routeBuilder?: UmbModalRouteBuilder;
#urlBuilderCallback: ((urlBuilder: UmbModalRouteBuilder) => void) | undefined;
// Notice i removed the key in the transferring to this class.
constructor(
modalAlias: UmbModalToken<UmbModalTokenData, UmbModalTokenResult> | string,
modalAlias: UmbModalToken<UmbModalTokenData, UmbModalTokenValue> | string,
path: string | null = null,
modalConfig?: UmbModalConfig,
) {
@@ -87,11 +102,18 @@ export class UmbModalRouteRegistration<UmbModalTokenData extends object = object
this.#urlBuilderCallback?.(urlBuilder);
}
public onSetup(callback: (routingInfo: Params) => Promise<UmbModalTokenData | false> | UmbModalTokenData | false) {
public onSetup(
callback: (
routingInfo: Params,
) =>
| Promise<UmbModalRouteSetupReturn<UmbModalTokenData, UmbModalTokenValue> | false>
| UmbModalRouteSetupReturn<UmbModalTokenData, UmbModalTokenValue>
| false,
) {
this.#onSetupCallback = callback;
return this;
}
public onSubmit(callback: (data: UmbModalTokenResult) => void) {
public onSubmit(callback: (value: UmbModalTokenValue) => void) {
this.#onSubmitCallback = callback;
return this;
}
@@ -100,7 +122,7 @@ export class UmbModalRouteRegistration<UmbModalTokenData extends object = object
return this;
}
#onSubmit = (data: UmbModalTokenResult) => {
#onSubmit = (data: UmbModalTokenValue) => {
this.#onSubmitCallback?.(data);
this.#modalManagerContext = undefined;
};
@@ -115,7 +137,14 @@ export class UmbModalRouteRegistration<UmbModalTokenData extends object = object
const modalData = this.#onSetupCallback ? await this.#onSetupCallback(params) : undefined;
if (modalData !== false) {
this.#modalManagerContext = modalManagerContext.open(this.#modalAlias, modalData, this.modalConfig, router);
this.#modalManagerContext = modalManagerContext.open(this.#modalAlias, {
data: {
data: modalData?.data,
value: modalData?.value,
config: this.modalConfig,
router,
},
});
this.#modalManagerContext.onSubmit().then(this.#onSubmit, this.#onReject);
return this.#modalManagerContext;
}

View File

@@ -6,36 +6,22 @@ import { UmbId } from '@umbraco-cms/backoffice/id';
import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
/**
* Type which omits the real submit method, and replaces it with a submit method which accepts an optional argument depending on the generic type.
*/
export type UmbModalContext<ModalData extends object = object, ModalValue = any> = Omit<
UmbModalContextClass<ModalData, ModalValue>,
'submit'
> &
OptionalSubmitArgumentIfUndefined<ModalValue>;
// If Type is undefined we don't accept an argument,
// If type is unknown, we accept an option argument.
// If type is anything else, we require an argument of that type.
type OptionalSubmitArgumentIfUndefined<T> = T extends undefined
? {
submit: () => void;
}
: T extends unknown
? {
submit: (arg?: T) => void;
}
: {
submit: (arg: T) => void;
};
export interface UmbModalRejectReason {
type: string;
}
export type UmbModalContextClassArgs<
ModalAliasType extends string | UmbModalToken,
ModalAliasTypeAsToken extends UmbModalToken = ModalAliasType extends UmbModalToken ? ModalAliasType : UmbModalToken,
> = {
router?: IRouterSlot | null;
data?: ModalAliasTypeAsToken['DATA'];
value?: ModalAliasTypeAsToken['VALUE'];
config?: UmbModalConfig;
};
// TODO: consider splitting this into two separate handlers
export class UmbModalContextClass<ModalPreset extends object = object, ModalValue = unknown> extends EventTarget {
export class UmbModalContext<ModalPreset extends object = object, ModalValue = any> extends EventTarget {
#submitPromise: Promise<ModalValue>;
#submitResolver?: (value: ModalValue) => void;
#submitRejecter?: (reason?: UmbModalRejectReason) => void;
@@ -47,18 +33,16 @@ export class UmbModalContextClass<ModalPreset extends object = object, ModalValu
public readonly router: IRouterSlot | null = null;
public readonly alias: string | UmbModalToken<ModalPreset, ModalValue>;
#value = new UmbObjectState<ModalValue | undefined>(undefined);
public readonly value = this.#value.asObservable();
#value;
public readonly value;
constructor(
router: IRouterSlot | null,
modalAlias: string | UmbModalToken<ModalPreset, ModalValue>,
data?: ModalPreset,
config?: UmbModalConfig,
args: UmbModalContextClassArgs<UmbModalToken>,
) {
super();
this.key = config?.key || UmbId.new();
this.router = router;
this.key = args.config?.key || UmbId.new();
this.router = args.router ?? null;
this.alias = modalAlias;
if (this.alias instanceof UmbModalToken) {
@@ -66,11 +50,17 @@ export class UmbModalContextClass<ModalPreset extends object = object, ModalValu
this.size = this.alias.getDefaultConfig()?.size || this.size;
}
this.type = config?.type || this.type;
this.size = config?.size || this.size;
this.type = args.config?.type || this.type;
this.size = args.config?.size || this.size;
const defaultData = this.alias instanceof UmbModalToken ? this.alias.getDefaultData() : undefined;
this.data = Object.freeze({ ...defaultData, ...data } as ModalPreset);
this.data = Object.freeze({ ...defaultData, ...args.data } as ModalPreset);
const initValue =
args.value ?? this.alias instanceof UmbModalToken ? (this.alias as UmbModalToken).getDefaultValue() : undefined;
this.#value = new UmbObjectState(initValue) as UmbObjectState<ModalValue>;
this.value = this.#value.asObservable();
// TODO: Consider if its right to use Promises, or use another event based system? Would we need to be able to cancel an event, to then prevent the closing..?
this.#submitPromise = new Promise((resolve, reject) => {
@@ -85,8 +75,8 @@ export class UmbModalContextClass<ModalPreset extends object = object, ModalValu
* @public
* @memberof UmbModalContext
*/
private submit(value?: ModalValue) {
this.#submitResolver?.(value as ModalValue);
public submit() {
this.#submitResolver?.(this.getValue());
}
/**
@@ -100,15 +90,17 @@ export class UmbModalContextClass<ModalPreset extends object = object, ModalValu
/**
* Gives a Promise which will be resolved when this modal is submitted.
* @returns {Promise<ModalValue>}
* @public
* @memberof UmbModalContext
*/
public onSubmit(): Promise<ModalValue> {
public onSubmit() {
return this.#submitPromise;
}
/**
* Gives the current value of this modal.
* @returns {ModalValue}
* @public
* @memberof UmbModalContext
*/

View File

@@ -1,6 +1,5 @@
export interface UmbPickerModalData<ItemType> {
multiple?: boolean;
selection?: Array<string | null>;
hideTreeRoot?: boolean;
filter?: (item: ItemType) => boolean;
pickableFilter?: (item: ItemType) => boolean;

View File

@@ -4,10 +4,12 @@ export interface UmbDataTypePickerFlowDataTypePickerModalData {
propertyEditorUiAlias: string;
}
export type UmbDataTypePickerFlowDataTypePickerModalValue = {
dataTypeId?: string;
createNewWithPropertyEditorUiAlias?: string;
};
export type UmbDataTypePickerFlowDataTypePickerModalValue =
| {
dataTypeId?: string;
createNewWithPropertyEditorUiAlias?: string;
}
| undefined;
export const UMB_DATA_TYPE_PICKER_FLOW_DATA_TYPE_PICKER_MODAL = new UmbModalToken<
UmbDataTypePickerFlowDataTypePickerModalData,

View File

@@ -1,7 +1,6 @@
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
export interface UmbDataTypePickerFlowModalData {
selection?: Array<string>;
submitLabel?: string;
}

View File

@@ -1,6 +1,6 @@
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
export interface UmbIconPickerModalData {}
export type UmbIconPickerModalData = never;
export interface UmbIconPickerModalValue {
color: string | undefined;

View File

@@ -3,7 +3,6 @@ import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
export interface UmbLanguagePickerModalData {
multiple?: boolean;
selection?: Array<string | null>;
filter?: (language: LanguageResponseModel) => boolean;
}

View File

@@ -2,12 +2,11 @@ import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
export interface UmbLinkPickerModalData {
index: number | null;
link: UmbLinkPickerLink;
config: UmbLinkPickerConfig;
index: number | null;
}
export type UmbLinkPickerModalValue = { index: number | null; link: UmbLinkPickerLink };
export type UmbLinkPickerModalValue = { link: UmbLinkPickerLink };
export interface UmbLinkPickerLink {
icon?: string | null;

View File

@@ -13,15 +13,15 @@ export class UmbModalToken<ModalDataType extends object = object, ModalValueType
readonly DATA: ModalDataType = undefined as never;
/**
* Get the result type of the token
* Get the value type of the token
*
* @public
* @type {ModalValueType}
* @memberOf UmbModalToken
* @example `typeof MyModal.RESULT`
* @example `typeof MyModal.VALUE`
* @returns undefined
*/
readonly RESULT: ModalValueType = undefined as never;
readonly VALUE: ModalValueType = undefined as never;
/**
* @param alias Unique identifier for the token,
@@ -32,6 +32,7 @@ export class UmbModalToken<ModalDataType extends object = object, ModalValueType
protected alias: string,
protected defaultConfig?: UmbModalConfig,
protected defaultData?: ModalDataType,
protected defaultValue?: ModalValueType,
) {}
/**
@@ -51,4 +52,8 @@ export class UmbModalToken<ModalDataType extends object = object, ModalValueType
public getDefaultData(): ModalDataType | undefined {
return this.defaultData;
}
public getDefaultValue(): ModalValueType | undefined {
return this.defaultValue;
}
}

View File

@@ -1,7 +1,6 @@
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
export interface UmbPropertyEditorUIPickerModalData {
selection?: Array<string>;
submitLabel?: string;
}

View File

@@ -3,7 +3,6 @@ import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
export type UmbPropertySettingsModalData = {
documentTypeId: string;
propertyData: PropertyTypeModelBaseModel;
};
export type UmbPropertySettingsModalValue = PropertyTypeModelBaseModel;

View File

@@ -6,11 +6,13 @@ export interface UmbWorkspaceData {
preset: Partial<CreateDataTypeRequestModel>;
}
export type UmbWorkspaceResult = {
id: string;
};
export type UmbWorkspaceValue =
| {
id: string;
}
| undefined;
export const UMB_WORKSPACE_MODAL = new UmbModalToken<UmbWorkspaceData, UmbWorkspaceResult>('Umb.Modal.Workspace', {
export const UMB_WORKSPACE_MODAL = new UmbModalToken<UmbWorkspaceData, UmbWorkspaceValue>('Umb.Modal.Workspace', {
type: 'sidebar',
size: 'large',
});

View File

@@ -13,6 +13,7 @@ import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-ap
import { ItemResponseModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
export class UmbPickerInputContext<ItemType extends ItemResponseModelBaseModel> extends UmbBaseController {
// TODO: We are way too unsecure about the requirements for the Modal Token, as we have certain expectation for the data and value.
modalAlias: string | UmbModalToken;
repository?: UmbItemRepository<ItemType>;
#getUnique: (entry: ItemType) => string | undefined;
@@ -68,11 +69,16 @@ export class UmbPickerInputContext<ItemType extends ItemResponseModelBaseModel>
openPicker(pickerData?: Partial<UmbPickerModalData<ItemType>>) {
if (!this.modalManager) throw new Error('Modal manager context is not initialized');
// TODO: Update so selection is part of value...
const modalContext = this.modalManager.open(this.modalAlias, {
multiple: this.max === 1 ? false : true,
selection: [...this.getSelection()],
pickableFilter: this.pickableFilter,
...pickerData,
data: {
multiple: this.max === 1 ? false : true,
pickableFilter: this.pickableFilter,
...pickerData,
},
value: {
selection: [...this.getSelection()],
},
});
modalContext?.onSubmit().then(({ selection }: any) => {
@@ -87,10 +93,12 @@ export class UmbPickerInputContext<ItemType extends ItemResponseModelBaseModel>
if (!item) throw new Error('Could not find item with unique: ' + unique);
const modalContext = this.modalManager?.open(UMB_CONFIRM_MODAL, {
color: 'danger',
headline: `Remove ${item.name}?`,
content: 'Are you sure you want to remove this item',
confirmLabel: 'Remove',
data: {
color: 'danger',
headline: `Remove ${item.name}?`,
content: 'Are you sure you want to remove this item',
confirmLabel: 'Remove',
},
});
await modalContext?.onSubmit();

View File

@@ -28,9 +28,11 @@ export default class UmbTinyMceCodeEditorPlugin extends UmbTinyMcePluginBase {
const modalHandler = this.#modalContext?.open<UmbCodeEditorModalData, UmbCodeEditorModalValue>(
UMB_CODE_EDITOR_MODAL,
{
headline: 'Edit source code',
content: this.editor.getContent() ?? '',
language: 'html',
data: {
headline: 'Edit source code',
content: this.editor.getContent() ?? '',
language: 'html',
},
},
);

View File

@@ -77,7 +77,7 @@ export default class UmbTinyMceEmbeddedMediaPlugin extends UmbTinyMcePluginBase
}
async #showModal(selectedElm: HTMLElement, embeddedMediaModalData: UmbEmbeddedMediaModalData) {
const modalHandler = this.#modalContext?.open(UMB_EMBEDDED_MEDIA_MODAL, embeddedMediaModalData);
const modalHandler = this.#modalContext?.open(UMB_EMBEDDED_MEDIA_MODAL, { data: embeddedMediaModalData });
if (!modalHandler) return;

View File

@@ -94,11 +94,15 @@ export default class UmbTinyMceLinkPickerPlugin extends UmbTinyMcePluginBase {
async #openLinkPicker(currentTarget?: UmbLinkPickerLink) {
const modalHandler = this.#modalContext?.open(UMB_LINK_PICKER_MODAL, {
config: {
ignoreUserStartNodes: this.configuration?.getValueByAlias<boolean>('ignoreUserStartNodes') ?? false,
data: {
config: {
ignoreUserStartNodes: this.configuration?.getValueByAlias<boolean>('ignoreUserStartNodes') ?? false,
},
index: null,
},
value: {
link: currentTarget ?? {},
},
link: currentTarget ?? {},
index: null,
});
if (!modalHandler) return;

View File

@@ -193,8 +193,10 @@ export default class UmbTinyMceMacroPickerPlugin extends UmbTinyMcePluginBase {
// TODO => depends on macro picker, which doesn't exist, just showing a generic modal for now
async #showMacroPicker(dialogData: DialogData) {
const modalHandler = this.#modalContext?.open(UMB_CONFIRM_MODAL, {
headline: 'Macro picker',
content: 'Yet to be implemented',
data: {
headline: 'Macro picker',
content: 'Yet to be implemented',
},
});
if (!modalHandler) return;

View File

@@ -54,11 +54,13 @@ export default class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase {
});
}
/*
async #observeCurrentUser() {
if (!this.#currentUserContext) return;
this.observe(this.#currentUserContext.currentUser, (currentUser) => (this.#currentUser = currentUser));
}
*/
async #onAction() {
const selectedElm = this.editor.selection.getNode();
@@ -90,6 +92,8 @@ export default class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase {
}
async #showMediaPicker(currentTarget: MediaPickerTargetData) {
/*
// TODO: I dont think we should parse this one... it should be up to the modal to get this information, and then we could parse some configs on to affect this.
let startNodeId;
let startNodeIsVirtual;
@@ -102,13 +106,18 @@ export default class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase {
startNodeIsVirtual = this.#currentUser?.mediaStartNodeIds?.length !== 1;
}
}
*/
// TODO => startNodeId and startNodeIsVirtual do not exist on ContentTreeItemResponseModel
const modalHandler = this.#modalContext?.open(UMB_MEDIA_TREE_PICKER_MODAL, {
selection: currentTarget.udi ? [...currentTarget.udi] : [],
multiple: false,
//startNodeId,
//startNodeIsVirtual,
data: {
multiple: false,
//startNodeId,
//startNodeIsVirtual,
},
value: {
selection: currentTarget.udi ? [...currentTarget.udi] : [],
},
});
if (!modalHandler) return;

View File

@@ -49,7 +49,8 @@ export abstract class UmbEditableWorkspaceContextBase<RepositoryType, EntityType
}
protected submitModal(data: EntityType) {
this.modalContext?.submit(data);
this.modalContext?.setValue(data);
this.modalContext?.submit();
}
abstract getEntityId(): string | undefined; // TODO: Consider if this should go away/be renamed? now that we have getUnique()

View File

@@ -41,10 +41,11 @@ export class UmbCreateDictionaryModalElement extends UmbModalBaseElement<
const formData = new FormData(form);
const name = formData.get('name') as string;
this.modalContext?.submit({
this._value = {
name,
parentId: this.data?.parentId ?? null,
});
};
this.modalContext?.submit();
}
render() {

View File

@@ -30,7 +30,8 @@ export class UmbExportDictionaryModalElement extends UmbModalBaseElement<
const formData = new FormData(form);
this.modalContext?.submit({ includeChildren: (formData.get('includeDescendants') as string) === 'on' });
this._value = { includeChildren: (formData.get('includeDescendants') as string) === 'on' };
this.modalContext?.submit();
}
render() {

View File

@@ -24,7 +24,7 @@ export default class UmbExportDictionaryEntityAction extends UmbEntityActionBase
async execute() {
if (!this.#modalContext) return;
const modalContext = this.#modalContext?.open(UMB_EXPORT_DICTIONARY_MODAL, { unique: this.unique });
const modalContext = this.#modalContext?.open(UMB_EXPORT_DICTIONARY_MODAL, { data: { unique: this.unique } });
const { includeChildren } = await modalContext.onSubmit();
if (includeChildren === undefined) return;

View File

@@ -71,7 +71,8 @@ export class UmbImportDictionaryModalLayout extends UmbModalBaseElement<
const { error } = await this.#dictionaryRepository.import(this._temporaryFileId, this._parentId);
if (error) return;
this.modalContext?.submit({ entityItems: this.#createTreeEntitiesFromTempFile(), parentId: this._parentId });
this._value = { entityItems: this.#createTreeEntitiesFromTempFile(), parentId: this._parentId };
this.modalContext?.submit();
}
constructor() {

View File

@@ -29,7 +29,7 @@ export default class UmbImportDictionaryEntityAction extends UmbEntityActionBase
async execute() {
if (!this.#modalContext) return;
const modalContext = this.#modalContext?.open(UMB_IMPORT_DICTIONARY_MODAL, { unique: this.unique });
const modalContext = this.#modalContext?.open(UMB_IMPORT_DICTIONARY_MODAL, { data: { unique: this.unique } });
const { entityItems, parentId } = await modalContext.onSubmit();

View File

@@ -89,16 +89,18 @@ export class UmbDashboardRedirectManagementElement extends UmbLitElement {
#onRequestDelete(data: RedirectUrlResponseModel) {
if (!data.id) return;
const modalContext = this._modalContext?.open(UMB_CONFIRM_MODAL, {
headline: 'Delete',
content: html`
<div style="width:300px">
<p>${this.localize.term('redirectUrls_redirectRemoveWarning')}</p>
${this.localize.term('redirectUrls_originalUrl')}: <strong>${data.originalUrl}</strong><br />
${this.localize.term('redirectUrls_redirectedTo')}: <strong>${data.destinationUrl}</strong>
</div>
`,
color: 'danger',
confirmLabel: 'Delete',
data: {
headline: 'Delete',
content: html`
<div style="width:300px">
<p>${this.localize.term('redirectUrls_redirectRemoveWarning')}</p>
${this.localize.term('redirectUrls_originalUrl')}: <strong>${data.originalUrl}</strong><br />
${this.localize.term('redirectUrls_redirectedTo')}: <strong>${data.destinationUrl}</strong>
</div>
`,
color: 'danger',
confirmLabel: 'Delete',
},
});
modalContext
@@ -136,10 +138,12 @@ export class UmbDashboardRedirectManagementElement extends UmbLitElement {
}
const modalContext = this._modalContext?.open(UMB_CONFIRM_MODAL, {
headline: `${this.localize.term('redirectUrls_disableUrlTracker')}`,
content: `${this.localize.term('redirectUrls_confirmDisable')}`,
color: 'danger',
confirmLabel: 'Disable',
data: {
headline: `${this.localize.term('redirectUrls_disableUrlTracker')}`,
content: `${this.localize.term('redirectUrls_confirmDisable')}`,
color: 'danger',
confirmLabel: 'Disable',
},
});
modalContext
?.onSubmit()

View File

@@ -20,7 +20,9 @@ export class UmbCreateDataTypeEntityAction extends UmbEntityActionBase<UmbDocume
if (!this.repository) throw new Error('Repository is not available');
this.#modalManagerContext?.open(UMB_DOCUMENT_TYPE_CREATE_OPTIONS_MODAL, {
parentKey: this.unique,
data: {
parentKey: this.unique,
},
});
}
}

View File

@@ -30,7 +30,9 @@ export class UmbDataTypeCreateOptionsModalElement extends UmbLitElement {
#onClick(event: PointerEvent) {
event.stopPropagation();
const folderModalHandler = this.#modalContext?.open(UMB_FOLDER_MODAL, {
repositoryAlias: DOCUMENT_TYPE_DETAIL_REPOSITORY_ALIAS,
data: {
repositoryAlias: DOCUMENT_TYPE_DETAIL_REPOSITORY_ALIAS,
},
});
folderModalHandler?.onSubmit().then(() => this.modalContext?.submit());
}

View File

@@ -101,8 +101,10 @@ export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
private async _handleIconClick() {
const modalContext = this._modalContext?.open(UMB_ICON_PICKER_MODAL, {
icon: this._icon,
color: this._iconColorAlias,
value: {
icon: this._icon,
color: this._iconColorAlias,
},
});
modalContext?.onSubmit().then((saved) => {

View File

@@ -120,10 +120,10 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle
if (documentTypeId === undefined) return false;
const propertyData = await this._propertyStructureHelper.createPropertyScaffold(this._containerId);
if (propertyData === undefined) return false;
return { propertyData, documentTypeId };
return { data: { documentTypeId }, value: propertyData };
})
.onSubmit((result) => {
this.#addProperty(result);
.onSubmit((value) => {
this.#addProperty(value);
})
.observeRouteBuilder((routeBuilder) => {
this._modalRouteNewProperty = routeBuilder(null);

View File

@@ -92,7 +92,7 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement {
if (documentTypeId === undefined) return false;
const propertyData = this.property;
if (propertyData === undefined) return false;
return { propertyData, documentTypeId };
return { data: { documentTypeId }, value: propertyData };
})
.onSubmit((result) => {
this._partialUpdate(result);
@@ -104,7 +104,7 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement {
new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL)
.addAdditionalPath('document-type')
.onSetup(() => {
return { entityType: 'document-type', preset: {} };
return { data: { entityType: 'document-type', preset: {} } };
})
.observeRouteBuilder((routeBuilder) => {
this._editDocumentTypePath = routeBuilder({});
@@ -138,7 +138,7 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement {
e.stopImmediatePropagation();
if (!this.property || !this.property.id) return;
const Message: UmbConfirmModalData = {
const modalData: UmbConfirmModalData = {
headline: `${this.localize.term('actions_delete')} property`,
content: html`<umb-localize key="contentTypeEditor_confirmDeletePropertyMessage" .args=${[
this.property.name || this.property.id,
@@ -150,7 +150,7 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement {
color: 'danger',
};
const modalHandler = this._modalManagerContext?.open(UMB_CONFIRM_MODAL, Message);
const modalHandler = this._modalManagerContext?.open(UMB_CONFIRM_MODAL, { data: modalData });
modalHandler
?.onSubmit()

View File

@@ -186,7 +186,7 @@ export class UmbDocumentTypeWorkspaceViewEditElement extends UmbLitElement imple
}
#requestRemoveTab(tab: PropertyTypeContainerModelBaseModel | undefined) {
const Message: UmbConfirmModalData = {
const modalData: UmbConfirmModalData = {
headline: 'Delete tab',
content: html`<umb-localize key="contentTypeEditor_confirmDeleteTabMessage" .args=${[tab?.name ?? tab?.id]}>
Are you sure you want to delete the tab <strong>${tab?.name ?? tab?.id}</strong>
@@ -202,7 +202,7 @@ export class UmbDocumentTypeWorkspaceViewEditElement extends UmbLitElement imple
// TODO: If this tab is composed of other tabs, then notify that it will only delete the local tab.
const modalHandler = this._modalManagerContext?.open(UMB_CONFIRM_MODAL, Message);
const modalHandler = this._modalManagerContext?.open(UMB_CONFIRM_MODAL, { data: modalData });
modalHandler?.onSubmit().then(() => {
this.#remove(tab?.id);

View File

@@ -61,11 +61,14 @@ export class UmbInputDocumentGranularPermissionElement extends FormControlMixin(
#openDocumentPicker() {
// We send a shallow copy(good enough as its just an array of ids) of our this._selectedIds, as we don't want the modal to manipulate our data:
// TODO: Use value instead:
const modalContext = this.#modalContext?.open(UMB_DOCUMENT_PICKER_MODAL, {
selection: [...this._selectedIds],
value: {
selection: [...this._selectedIds],
},
});
modalContext?.onSubmit().then(({ selection }: any) => {
modalContext?.onSubmit().then((value) => {
//this.#setSelection(selection);
});
}

View File

@@ -58,7 +58,8 @@ export class UmbCreateDocumentModalElement extends UmbModalBaseElement<
const target = event.target as HTMLButtonElement;
const documentTypeId = target.dataset.id;
if (!documentTypeId) throw new Error('No document type id found');
this.modalContext?.submit({ documentTypeId });
this._value = { documentTypeId };
this.modalContext?.submit();
}
render() {

View File

@@ -16,7 +16,8 @@ export default class UmbLogViewerSaveSearchModalElement extends UmbModalBaseElem
}
private _handleSubmit() {
this.modalContext?.submit({ name: this._input.value as string, query: this.data?.query });
this._value = { name: this._input.value as string, query: this.data?.query };
this.modalContext?.submit();
}
@state()

View File

@@ -102,12 +102,14 @@ export class UmbMediaTypeWorkspaceEditorElement extends UmbLitElement {
private async _handleIconClick() {
const modalContext = this._modalContext?.open(UMB_ICON_PICKER_MODAL, {
icon: this._icon,
color: this._iconColorAlias,
value: {
icon: this._icon,
color: this._iconColorAlias,
},
});
modalContext?.onSubmit().then((saved) => {
if (saved.icon) this.#workspaceContext?.updateProperty('icon', saved.icon);
modalContext?.onSubmit().then((value) => {
if (value.icon) this.#workspaceContext?.updateProperty('icon', value.icon);
// TODO: save color ALIAS as well
});
}

View File

@@ -118,10 +118,10 @@ export class UmbMediaTypeWorkspaceViewEditPropertiesElement extends UmbLitElemen
if (mediaTypeId === undefined) return false;
const propertyData = await this._propertyStructureHelper.createPropertyScaffold(this._containerId);
if (propertyData === undefined) return false;
return { propertyData, documentTypeId: mediaTypeId }; //TODO: Should we have a separate modal for mediaTypes?
return { data: { documentTypeId: mediaTypeId }, value: propertyData }; //TODO: Should we have a separate modal for mediaTypes?
})
.onSubmit((result) => {
this.#addProperty(result);
.onSubmit((value) => {
this.#addProperty(value);
})
.observeRouteBuilder((routeBuilder) => {
this._modalRouteNewProperty = routeBuilder(null);

View File

@@ -22,8 +22,12 @@ export class UmbMediaMoveEntityBulkAction extends UmbEntityBulkActionBase<UmbMed
async execute() {
// TODO: the picker should be single picker by default
const modalContext = this.#modalContext?.open(UMB_MEDIA_TREE_PICKER_MODAL, {
selection: [],
multiple: false,
data: {
multiple: false,
},
value: {
selection: [],
},
});
if (modalContext) {
const { selection } = await modalContext.onSubmit();

View File

@@ -16,7 +16,8 @@ export default class UmbExamineFieldsSettingsModalElement
private _fields?: UmbExamineFieldsSettingsModalData;
private _handleClose() {
this.modalContext?.submit({ fields: this._fields });
this._value = { fields: this._fields };
this.modalContext?.submit();
}
disconnectedCallback(): void {

View File

@@ -23,7 +23,7 @@ export class UmbLanguagePickerModalElement extends UmbModalBaseElement<
connectedCallback(): void {
super.connectedCallback();
this.#selectionManager.setMultiple(this.data?.multiple ?? false);
this.#selectionManager.setSelection(this.data?.selection ?? []);
this.#selectionManager.setSelection(this._value?.selection ?? []);
}
async firstUpdated() {
@@ -40,7 +40,8 @@ export class UmbLanguagePickerModalElement extends UmbModalBaseElement<
}
#submit() {
this.modalContext?.submit({ selection: this.#selectionManager.getSelection() });
this._value = { selection: this.#selectionManager.getSelection() };
this.modalContext?.submit();
}
#close() {

View File

@@ -75,7 +75,9 @@ export class UmbTemplatingInsertMenuElement extends UmbLitElement {
#openChooseTypeModal = () => {
this.#openModal = this._modalContext?.open(UMB_MODAL_TEMPLATING_INSERT_CHOOSE_TYPE_SIDEBAR_MODAL, {
hidePartialView: this.hidePartialView,
data: {
hidePartialView: this.hidePartialView,
},
});
this.#openModal?.onSubmit().then((closedModal: UmbChooseInsertTypeModalValue) => {
this.determineInsertValue(closedModal);
@@ -93,7 +95,9 @@ export class UmbTemplatingInsertMenuElement extends UmbLitElement {
#openInsertDictionaryItemModal() {
this.#openModal = this._modalContext?.open(UMB_DICTIONARY_ITEM_PICKER_MODAL, {
pickableFilter: (item) => item.id !== null,
data: {
pickableFilter: (item) => item.id !== null,
},
});
this.#openModal?.onSubmit().then((value) => {
this.#getDictionaryItemSnippet(value).then(() => {

View File

@@ -47,21 +47,27 @@ export default class UmbChooseInsertTypeModalElement extends UmbModalBaseElement
#openInsertPartialViewSidebar() {
this.#openModal = this._modalContext?.open(UMB_PARTIAL_VIEW_PICKER_MODAL);
this.#openModal?.onSubmit().then((partialViewPickerModalValue) => {
if (partialViewPickerModalValue)
this.modalContext?.submit({
if (partialViewPickerModalValue) {
this._value = {
type: CodeSnippetType.partialView,
value: partialViewPickerModalValue.selection[0],
});
};
this.modalContext?.submit();
}
});
}
#openInsertDictionaryItemModal() {
this.#openModal = this._modalContext?.open(UMB_DICTIONARY_ITEM_PICKER_MODAL, {
pickableFilter: (item) => item.id !== null,
data: {
pickableFilter: (item) => item.id !== null,
},
});
this.#openModal?.onSubmit().then((dictionaryItemPickerModalValue) => {
if (dictionaryItemPickerModalValue)
this.modalContext?.submit({ value: dictionaryItemPickerModalValue, type: CodeSnippetType.dictionaryItem });
if (dictionaryItemPickerModalValue) {
this._value = { value: dictionaryItemPickerModalValue, type: CodeSnippetType.dictionaryItem };
this.modalContext?.submit();
}
});
}

View File

@@ -64,7 +64,10 @@ export default class UmbTemplatingInsertSectionModalElement extends UmbModalBase
#submit() {
const value = this.selectedCheckbox?.snippet;
if (this.selectedCheckbox?.validate()) this.modalContext?.submit({ value: value ?? '' });
if (this.selectedCheckbox?.validate()) {
this._value = { value: value ?? '' };
this.modalContext?.submit();
}
}
render() {

View File

@@ -20,7 +20,7 @@ export default class UmbPartialViewPickerModalElement extends UmbModalBaseElemen
connectedCallback() {
super.connectedCallback();
this._selection = this.data?.selection ?? [];
this._selection = this._value?.selection ?? [];
this._multiple = this.data?.multiple ?? true;
}
@@ -32,7 +32,8 @@ export default class UmbPartialViewPickerModalElement extends UmbModalBaseElemen
}
private _submit() {
this.modalContext?.submit({ selection: this._selection });
this._value = { selection: this._selection };
this.modalContext?.submit();
}
private _close() {

View File

@@ -30,7 +30,9 @@ export default class UmbStylesheetRichTextEditorRuleElement extends UmbLitElemen
openModal = () => {
if (!this._modalContext) throw new Error('Modal context not found');
const modal = this._modalContext.open(UMB_MODAL_TEMPLATING_STYLESHEET_RTF_STYLE_SIDEBAR_MODAL, {
rule: this.rule,
value: {
rule: this.rule,
},
});
modal?.onSubmit().then((result) => {
if (result.rule && this.rule?.name) {

View File

@@ -4,57 +4,48 @@ import { css, html, customElement, ifDefined, state } from '@umbraco-cms/backoff
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
import { RichTextRuleModel } from '@umbraco-cms/backoffice/backend-api';
export interface StylesheetRichTextEditorStyleModalData {
export interface StylesheetRichTextEditorStyleModalValue {
rule: RichTextRuleModelSortable | null;
}
export type UmbStylesheetRichTextEditorStyleModalValue = NonNullable<Required<StylesheetRichTextEditorStyleModalData>>;
@customElement('umb-stylesheet-rich-text-editor-style-modal')
export default class UmbStylesheetRichTextEditorStyleModalElement extends UmbModalBaseElement<
StylesheetRichTextEditorStyleModalData,
UmbStylesheetRichTextEditorStyleModalValue
never,
StylesheetRichTextEditorStyleModalValue
> {
private _close() {
this.modalContext?.reject();
}
#submit() {
this.modalContext?.submit({ rule: this._rule });
}
connectedCallback() {
super.connectedCallback();
this._rule = this.data?.rule ?? null;
}
@state()
private _rule: RichTextRuleModel | null = null;
#updateName(event: Event) {
const name = (event.target as HTMLInputElement).value;
this._rule = {
...this._rule,
name,
this._value = {
rule: {
...this._value.rule,
name,
},
};
}
#updateSelector(event: Event) {
const selector = (event.target as HTMLInputElement).value;
this._rule = {
...this._rule,
selector,
this._value = {
rule: {
...this._value.rule,
selector,
},
};
}
#updateStyles(event: Event) {
const styles = (event.target as HTMLInputElement).value;
this._rule = {
...this._rule,
styles,
this._value = {
rule: {
...this._value.rule,
styles,
},
};
}
@@ -120,8 +111,8 @@ export default class UmbStylesheetRichTextEditorStyleModalElement extends UmbMod
</uui-box>
</div>
<div slot="actions">
<uui-button @click=${this._close} look="secondary" label="Close">Close</uui-button>
<uui-button @click=${this.#submit} look="primary" color="positive" label="Submit">Submit</uui-button>
<uui-button @click=${this._rejectModal} look="secondary" label="Close">Close</uui-button>
<uui-button @click=${this._submitModal} look="primary" color="positive" label="Submit">Submit</uui-button>
</div>
</umb-body-layout>
`;

View File

@@ -1,9 +1,6 @@
import { RichTextRuleModelSortable, UmbStylesheetWorkspaceContext } from '../../stylesheet-workspace.context.js';
import { UMB_MODAL_TEMPLATING_STYLESHEET_RTF_STYLE_SIDEBAR } from '../../manifests.js';
import {
StylesheetRichTextEditorStyleModalData,
UmbStylesheetRichTextEditorStyleModalValue,
} from './stylesheet-workspace-view-rich-text-editor-style-sidebar.element.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';
@@ -15,12 +12,17 @@ import { css, html, customElement, state, ifDefined, repeat } from '@umbraco-cms
import './stylesheet-workspace-view-rich-text-editor-rule.element.js';
export const UMB_MODAL_TEMPLATING_STYLESHEET_RTF_STYLE_SIDEBAR_MODAL = new UmbModalToken<
StylesheetRichTextEditorStyleModalData,
UmbStylesheetRichTextEditorStyleModalValue
>(UMB_MODAL_TEMPLATING_STYLESHEET_RTF_STYLE_SIDEBAR, {
type: 'sidebar',
size: 'medium',
});
never,
StylesheetRichTextEditorStyleModalValue
>(
UMB_MODAL_TEMPLATING_STYLESHEET_RTF_STYLE_SIDEBAR,
{
type: 'sidebar',
size: 'medium',
},
undefined,
{ rule: null },
);
const SORTER_CONFIG: UmbSorterConfig<RichTextRuleModel> = {
compareElementToModel: (element: HTMLElement, model: RichTextRuleModel) => {
@@ -73,7 +75,9 @@ export class UmbStylesheetWorkspaceViewRichTextEditorElement extends UmbLitEleme
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, {
rule,
value: {
rule,
},
});
modal?.onSubmit().then((result) => {
if (result.rule) {

View File

@@ -112,14 +112,18 @@ export class UmbInputTemplateElement extends FormControlMixin(UmbLitElement) {
#openPicker() {
// TODO: Change experience, so its not multi selectable. But instead already picked templates should be unpickable. (awaiting general picker features for such)
const modalContext = this._modalContext?.open(UMB_TEMPLATE_PICKER_MODAL, {
multiple: true,
selection: [...this._selectedIds],
pickableFilter: (template: TemplateResponseModel) => template.id !== null,
data: {
multiple: true,
pickableFilter: (template: TemplateResponseModel) => template.id !== null,
},
value: {
selection: [...this._selectedIds],
},
});
modalContext?.onSubmit().then((data) => {
if (!data.selection) return;
this.selectedIds = data.selection.filter((x) => x !== null) as Array<string>;
modalContext?.onSubmit().then((value) => {
if (!value?.selection) return;
this.selectedIds = value.selection.filter((x) => x !== null) as Array<string>;
this.dispatchEvent(new CustomEvent('change'));
});
}
@@ -141,8 +145,10 @@ export class UmbInputTemplateElement extends FormControlMixin(UmbLitElement) {
const id = (e.target as UmbTemplateCardElement).value;
this._modalContext?.open(UMB_TEMPLATE_MODAL, {
id: id as string,
language: 'razor',
data: {
id: id as string,
language: 'razor',
},
});
}

View File

@@ -80,9 +80,7 @@ export default class UmbChooseInsertTypeModalElement extends UmbModalBaseElement
}
#submit() {
this.modalContext?.submit({
value: this._templateQuery?.queryExpression ?? '',
});
this.modalContext?.submit();
}
#updateQueryRequest(update: TemplateQueryExecuteModel) {
@@ -99,12 +97,15 @@ export default class UmbChooseInsertTypeModalElement extends UmbModalBaseElement
const { data, error } = await this.#templateRepository.postTemplateQueryExecute({
requestBody: this._queryRequest,
});
if (data) this._templateQuery = { ...data };
if (data) {
this._templateQuery = { ...data };
this._value = { value: this._templateQuery?.queryExpression ?? '' };
}
};
#openDocumentPicker = () => {
this.#modalManagerContext
?.open(UMB_DOCUMENT_PICKER_MODAL, { hideTreeRoot: true })
?.open(UMB_DOCUMENT_PICKER_MODAL, { data: { hideTreeRoot: true } })
.onSubmit()
.then((result) => {
this.#updateQueryRequest({ rootContentId: result.selection[0] });

View File

@@ -112,7 +112,9 @@ export class UmbTemplateWorkspaceEditorElement extends UmbLitElement {
#openInsertSectionModal() {
const sectionModal = this._modalContext?.open(UMB_MODAL_TEMPLATING_INSERT_SECTION_MODAL);
sectionModal?.onSubmit().then((insertSectionModalValue) => {
if (insertSectionModalValue.value) this._codeEditor?.insert(insertSectionModalValue.value);
if (insertSectionModalValue?.value) {
this._codeEditor?.insert(insertSectionModalValue.value);
}
});
}
@@ -122,15 +124,19 @@ export class UmbTemplateWorkspaceEditorElement extends UmbLitElement {
#openMasterTemplatePicker() {
const modalContext = this._modalContext?.open(UMB_TEMPLATE_PICKER_MODAL, {
selection: [this.#masterTemplateId],
pickableFilter: (item) => {
return item.id !== null && item.id !== this.#templateWorkspaceContext?.getEntityId();
data: {
pickableFilter: (item) => {
return item.id !== null && item.id !== this.#templateWorkspaceContext?.getEntityId();
},
},
value: {
selection: [this.#masterTemplateId],
},
});
modalContext?.onSubmit().then((data) => {
if (!data.selection) return;
this.#templateWorkspaceContext?.setMasterTemplate(data.selection[0] ?? '');
modalContext?.onSubmit().then((value) => {
if (!value?.selection) return;
this.#templateWorkspaceContext?.setMasterTemplate(value.selection[0] ?? '');
});
}
@@ -138,7 +144,9 @@ export class UmbTemplateWorkspaceEditorElement extends UmbLitElement {
const queryBuilderModal = this._modalContext?.open(UMB_TEMPLATE_QUERY_BUILDER_MODAL);
queryBuilderModal?.onSubmit().then((queryBuilderModalValue) => {
if (queryBuilderModalValue.value) this._codeEditor?.insert(getQuerySnippet(queryBuilderModalValue.value));
if (queryBuilderModalValue?.value) {
this._codeEditor?.insert(getQuerySnippet(queryBuilderModalValue.value));
}
});
}

View File

@@ -51,7 +51,9 @@ export class UmbUserProfileAppProfileElement extends UmbLitElement {
if (!this.#modalManagerContext) return;
this.#modalManagerContext.open(UMB_CHANGE_PASSWORD_MODAL, {
userId: this._currentUser?.id ?? '',
data: {
userId: this._currentUser?.id ?? '',
},
});
}

View File

@@ -40,9 +40,10 @@ export class UmbChangePasswordModalElement extends UmbModalBaseElement<
// TODO: validate that the new password and confirm password match
const oldPassword = formData.get('oldPassword') as string;
const newPassword = formData.get('newPassword') as string;
const confirmPassword = formData.get('confirmPassword') as string;
//const confirmPassword = formData.get('confirmPassword') as string;
this.modalContext?.submit({ oldPassword, newPassword });
this._value = { oldPassword, newPassword };
this.modalContext?.submit();
}
constructor() {

View File

@@ -24,10 +24,12 @@ export class UmbDeleteUserGroupEntityBulkAction extends UmbEntityBulkActionBase<
if (!this.#modalContext || this.selection.length === 0) return;
const modalContext = this.#modalContext.open(UMB_CONFIRM_MODAL, {
color: 'danger',
headline: `Delete user groups?`,
content: html`Are you sure you want to delete selected user groups?`,
confirmLabel: 'Delete',
data: {
color: 'danger',
headline: `Delete user groups?`,
content: html`Are you sure you want to delete selected user groups?`,
confirmLabel: 'Delete',
},
});
await modalContext.onSubmit();

View File

@@ -1,25 +1,19 @@
import { html, customElement, property, css, nothing, state } from '@umbraco-cms/backoffice/external/lit';
import { html, customElement, css, nothing, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import {
UmbEntityUserPermissionSettingsModalData,
UmbEntityUserPermissionSettingsModalValue,
UmbModalContext,
UmbModalBaseElement,
} from '@umbraco-cms/backoffice/modal';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { UmbSelectionChangeEvent } from '@umbraco-cms/backoffice/event';
@customElement('umb-entity-user-permission-settings-modal')
export class UmbEntityUserPermissionSettingsModalElement extends UmbLitElement {
@property({ attribute: false })
modalContext?: UmbModalContext<UmbEntityUserPermissionSettingsModalData, UmbEntityUserPermissionSettingsModalValue>;
#data?: UmbEntityUserPermissionSettingsModalData;
@property({ type: Object })
get data(): UmbEntityUserPermissionSettingsModalData | undefined {
return this.#data;
}
export class UmbEntityUserPermissionSettingsModalElement extends UmbModalBaseElement<
UmbEntityUserPermissionSettingsModalData,
UmbEntityUserPermissionSettingsModalValue
> {
set data(data: UmbEntityUserPermissionSettingsModalData | undefined) {
super.data = data;
this._entityType = data?.entityType;
this._allowedPermissions = data?.allowedPermissions ?? [];
this._headline = data?.headline ?? this._headline;
@@ -35,11 +29,8 @@ export class UmbEntityUserPermissionSettingsModalElement extends UmbLitElement {
_allowedPermissions: Array<string> = [];
private _handleConfirm() {
this.modalContext?.submit({ allowedPermissions: this._allowedPermissions });
}
private _handleCancel() {
this.modalContext?.reject();
this._value = { allowedPermissions: this._allowedPermissions };
this.modalContext?.submit();
}
#onSelectedUserPermission(event: UmbSelectionChangeEvent) {
@@ -59,7 +50,7 @@ export class UmbEntityUserPermissionSettingsModalElement extends UmbLitElement {
: nothing}
</uui-box>
<uui-button slot="actions" id="cancel" label="Cancel" @click="${this._handleCancel}">Cancel</uui-button>
<uui-button slot="actions" id="cancel" label="Cancel" @click="${this._rejectModal}">Cancel</uui-button>
<uui-button
slot="actions"
id="confirm"

View File

@@ -22,7 +22,9 @@ export class UmbChangeUserPasswordEntityAction extends UmbEntityActionBase<UmbCh
if (!this.repository || !this.#modalManager) return;
const modalContext = this.#modalManager.open(UMB_CHANGE_PASSWORD_MODAL, {
userId: this.unique,
data: {
userId: this.unique,
},
});
const data = await modalContext.onSubmit();

View File

@@ -31,10 +31,12 @@ export class UmbDisableUserEntityAction extends UmbEntityActionBase<UmbDisableUs
const item = data[0];
const modalContext = this.#modalManager.open(UMB_CONFIRM_MODAL, {
headline: `Disable ${item.name}`,
content: 'Are you sure you want to disable this user?',
color: 'danger',
confirmLabel: 'Disable',
data: {
headline: `Disable ${item.name}`,
content: 'Are you sure you want to disable this user?',
color: 'danger',
confirmLabel: 'Disable',
},
});
await modalContext.onSubmit();

View File

@@ -31,9 +31,11 @@ export class UmbEnableUserEntityAction extends UmbEntityActionBase<UmbEnableUser
const item = data[0];
const modalContext = this.#modalManager.open(UMB_CONFIRM_MODAL, {
headline: `Enable ${item.name}`,
content: 'Are you sure you want to enable this user?',
confirmLabel: 'Enable',
data: {
headline: `Enable ${item.name}`,
content: 'Are you sure you want to enable this user?',
confirmLabel: 'Enable',
},
});
await modalContext.onSubmit();

View File

@@ -31,9 +31,11 @@ export class UmbUnlockUserEntityAction extends UmbEntityActionBase<UmbUnlockUser
const item = data[0];
const modalContext = this.#modalManager.open(UMB_CONFIRM_MODAL, {
headline: `Unlock ${item.name}`,
content: 'Are you sure you want to unlock this user?',
confirmLabel: 'Unlock',
data: {
headline: `Unlock ${item.name}`,
content: 'Are you sure you want to unlock this user?',
confirmLabel: 'Unlock',
},
});
await modalContext.onSubmit();

View File

@@ -24,10 +24,12 @@ export class UmbUserDeleteEntityBulkAction extends UmbEntityBulkActionBase<UmbUs
if (!this.#modalContext || this.selection.length === 0) return;
const modalContext = this.#modalContext.open(UMB_CONFIRM_MODAL, {
color: 'danger',
headline: `Delete users?`,
content: html`Are you sure you want to delete selected users?`,
confirmLabel: 'Delete',
data: {
color: 'danger',
headline: `Delete users?`,
content: html`Are you sure you want to delete selected users?`,
confirmLabel: 'Delete',
},
});
await modalContext.onSubmit();

View File

@@ -19,7 +19,9 @@ export class UmbResendInviteToUserEntityAction extends UmbEntityActionBase<UmbEn
if (!this.repository || !this.#modalManager) return;
const modalContext = this.#modalManager.open(UMB_RESEND_INVITE_TO_USER_MODAL, {
userId: this.unique,
data: {
userId: this.unique,
},
});
await modalContext.onSubmit();

View File

@@ -57,8 +57,10 @@ export class UmbUserCreateModalElement extends UmbModalBaseElement {
#openSuccessModal(userId: string, initialPassword: string) {
const modalContext = this.#modalManagerContext?.open(UMB_CREATE_USER_SUCCESS_MODAL, {
userId,
initialPassword,
data: {
userId,
initialPassword,
},
});
modalContext?.onSubmit().catch((reason) => {

View File

@@ -18,7 +18,7 @@ export class UmbUserPickerModalElement extends UmbModalBaseElement<UmbUserPicker
// TODO: in theory this config could change during the lifetime of the modal, so we could observe it
this.#selectionManager.setMultiple(this.data?.multiple ?? false);
this.#selectionManager.setSelection(this.data?.selection ?? []);
this.#selectionManager.setSelection(this._value?.selection ?? []);
}
protected firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
@@ -36,7 +36,8 @@ export class UmbUserPickerModalElement extends UmbModalBaseElement<UmbUserPicker
}
#submit() {
this.modalContext?.submit({ selection: this.#selectionManager.getSelection() });
this._value = { selection: this.#selectionManager.getSelection() };
this.modalContext?.submit();
}
#close() {

View File

@@ -192,7 +192,7 @@ class MyElement extends UmbElementMixin(LitElement) {
const options {'options go here'};
const modalContext = this.#modalManagerContext?.open(MY_MODAL_TOKEN), data, options);
modalContext?.onSubmit().then((data) => {
modalContext?.onSubmit().then((value) => {
// if modal submitted, then data is supplied here.
});
}
@@ -257,7 +257,8 @@ class MyDialog extends UmbElementMixin(LitElement) {
private _handleSubmit() {
/* Optional data of any type can be applied to the submit method to pass it
to the modal parent through the onSubmit promise. */
this.modalContext?.submit({ myReturnData: 'hello world' });
this.modalContext?.setValue({ myReturnData: 'hello world' });
this.modalContext?.submit();
}
render() {