deep partial modal data, for more fine grained definitions
This commit is contained in:
@@ -38,7 +38,7 @@ export class UmbModalManagerContext extends UmbContextBase<UmbModalManagerContex
|
||||
* @memberof UmbModalManagerContext
|
||||
*/
|
||||
public open<
|
||||
ModalData extends object = object,
|
||||
ModalData extends { [key: string]: any } = { [key: string]: any },
|
||||
ModalValue = unknown,
|
||||
ModalAliasTypeAsToken extends UmbModalToken = UmbModalToken<ModalData, ModalValue>,
|
||||
>(
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbId } from '@umbraco-cms/backoffice/id';
|
||||
import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import { type UmbDeepPartialObject, umbDeepMerge } from '@umbraco-cms/backoffice/utils';
|
||||
|
||||
export interface UmbModalRejectReason {
|
||||
type: string;
|
||||
@@ -13,7 +14,9 @@ export interface UmbModalRejectReason {
|
||||
|
||||
export type UmbModalContextClassArgs<
|
||||
ModalAliasType extends string | UmbModalToken,
|
||||
ModalAliasTypeAsToken extends UmbModalToken = ModalAliasType extends UmbModalToken ? ModalAliasType : UmbModalToken,
|
||||
ModalAliasTypeAsToken extends UmbModalToken = ModalAliasType extends UmbModalToken
|
||||
? ModalAliasType
|
||||
: UmbModalToken<never, never>,
|
||||
> = {
|
||||
router?: IRouterSlot | null;
|
||||
data?: ModalAliasTypeAsToken['DATA'];
|
||||
@@ -22,7 +25,10 @@ export type UmbModalContextClassArgs<
|
||||
};
|
||||
|
||||
// TODO: consider splitting this into two separate handlers
|
||||
export class UmbModalContext<ModalPreset extends object = object, ModalValue = any> extends UmbControllerBase {
|
||||
export class UmbModalContext<
|
||||
ModalPreset extends { [key: string]: any } = { [key: string]: any },
|
||||
ModalValue = any,
|
||||
> extends UmbControllerBase {
|
||||
//
|
||||
#submitPromise: Promise<ModalValue>;
|
||||
#submitResolver?: (value: ModalValue) => void;
|
||||
@@ -60,7 +66,13 @@ export class UmbModalContext<ModalPreset extends object = object, ModalValue = a
|
||||
this.backdropBackground = args.modal?.backdropBackground || this.backdropBackground;
|
||||
|
||||
const defaultData = this.alias instanceof UmbModalToken ? this.alias.getDefaultData() : undefined;
|
||||
this.data = Object.freeze({ ...defaultData, ...args.data } as ModalPreset);
|
||||
this.data = Object.freeze(
|
||||
// If we have both data and defaultData perform a deep merge
|
||||
args.data && defaultData
|
||||
? (umbDeepMerge(args.data as UmbDeepPartialObject<ModalPreset>, defaultData) as ModalPreset)
|
||||
: // otherwise pick one of them:
|
||||
(args.data as ModalPreset) ?? defaultData,
|
||||
);
|
||||
|
||||
const initValue =
|
||||
args.value ?? (this.alias instanceof UmbModalToken ? (this.alias as UmbModalToken).getDefaultValue() : undefined);
|
||||
|
||||
@@ -1,25 +1,30 @@
|
||||
import type { UmbModalToken } from '../token/index.js';
|
||||
import type { UmbModalConfig, UmbModalContext, UmbModalManagerContext, UmbModalRouteRegistration } from '../index.js';
|
||||
import type { UmbModalContextClassArgs } from '../context/modal.context.js';
|
||||
import { type Params, type IRouterSlot, UMB_ROUTE_CONTEXT, encodeFolderName } from '@umbraco-cms/backoffice/router';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import { UmbId } from '@umbraco-cms/backoffice/id';
|
||||
import type { UmbDeepPartialObject } from '@umbraco-cms/backoffice/utils';
|
||||
|
||||
export type UmbModalRouteBuilder = (params: { [key: string]: string | number } | null) => string;
|
||||
|
||||
export type UmbModalRouteSetupReturn<UmbModalTokenData, UmbModalTokenValue> = UmbModalTokenValue extends undefined
|
||||
? {
|
||||
modal?: UmbModalConfig;
|
||||
data: UmbModalTokenData;
|
||||
modal?: UmbDeepPartialObject<UmbModalConfig>;
|
||||
data: UmbDeepPartialObject<UmbModalTokenData>;
|
||||
value?: UmbModalTokenValue;
|
||||
}
|
||||
: {
|
||||
modal?: UmbModalConfig;
|
||||
data: UmbModalTokenData;
|
||||
modal?: UmbDeepPartialObject<UmbModalConfig>;
|
||||
data: UmbDeepPartialObject<UmbModalTokenData>;
|
||||
value: UmbModalTokenValue;
|
||||
};
|
||||
export class UmbModalRouteRegistrationController<UmbModalTokenData extends object = object, UmbModalTokenValue = any>
|
||||
export class UmbModalRouteRegistrationController<
|
||||
UmbModalTokenData extends { [key: string]: any } = { [key: string]: any },
|
||||
UmbModalTokenValue = any,
|
||||
>
|
||||
extends UmbControllerBase
|
||||
implements UmbModalRouteRegistration<UmbModalTokenData, UmbModalTokenValue>
|
||||
{
|
||||
@@ -293,8 +298,8 @@ export class UmbModalRouteRegistrationController<UmbModalTokenData extends objec
|
||||
modal: {},
|
||||
...modalData,
|
||||
router,
|
||||
};
|
||||
args.modal.key = this.#key;
|
||||
} as UmbModalContextClassArgs<UmbModalToken<UmbModalTokenData, UmbModalTokenValue>>;
|
||||
args.modal!.key = this.#key;
|
||||
|
||||
this.#modalContext = modalManagerContext.open(this, this.#modalAlias, args);
|
||||
this.#modalContext.onSubmit().then(this.#onSubmit, this.#onReject);
|
||||
|
||||
@@ -3,7 +3,10 @@ import type { UmbModalContext, UmbModalRouteBuilder } from '../index.js';
|
||||
import type { UmbModalToken } from '../token/modal-token.js';
|
||||
import type { IRouterSlot, Params } from '@umbraco-cms/backoffice/router';
|
||||
|
||||
export interface UmbModalRouteRegistration<UmbModalTokenData extends object = object, UmbModalTokenValue = any> {
|
||||
export interface UmbModalRouteRegistration<
|
||||
UmbModalTokenData extends { [key: string]: any } = { [key: string]: any },
|
||||
UmbModalTokenValue = any,
|
||||
> {
|
||||
key: string;
|
||||
alias: UmbModalToken<UmbModalTokenData, UmbModalTokenValue> | string;
|
||||
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import type { UmbModalConfig } from '../context/modal-manager.context.js';
|
||||
|
||||
export interface UmbModalTokenDefaults<ModalDataType extends object = object, ModalValueType = unknown> {
|
||||
export interface UmbModalTokenDefaults<
|
||||
ModalDataType extends { [key: string]: any } = { [key: string]: any },
|
||||
ModalValueType = unknown,
|
||||
> {
|
||||
modal?: UmbModalConfig;
|
||||
data?: ModalDataType;
|
||||
value?: ModalValueType;
|
||||
}
|
||||
|
||||
export class UmbModalToken<ModalDataType extends object = object, ModalValueType = unknown> {
|
||||
export class UmbModalToken<
|
||||
ModalDataType extends { [key: string]: any } = { [key: string]: any },
|
||||
ModalValueType = unknown,
|
||||
> {
|
||||
/**
|
||||
* Get the data type of the token's data.
|
||||
*
|
||||
|
||||
@@ -11,7 +11,7 @@ export interface UmbTreePickerModalCreateActionData<PathGeneratorType extends Um
|
||||
}
|
||||
|
||||
export interface UmbTreePickerModalData<
|
||||
TreeItemType = any,
|
||||
TreeItemType,
|
||||
PathGeneratorType extends UmbPathGeneratorType = UmbPathGeneratorType,
|
||||
> extends UmbPickerModalData<TreeItemType> {
|
||||
treeAlias?: string;
|
||||
@@ -21,7 +21,7 @@ export interface UmbTreePickerModalData<
|
||||
|
||||
export interface UmbTreePickerModalValue extends UmbPickerModalValue {}
|
||||
|
||||
export const UMB_TREE_PICKER_MODAL = new UmbModalToken<UmbTreePickerModalData, UmbTreePickerModalValue>(
|
||||
export const UMB_TREE_PICKER_MODAL = new UmbModalToken<UmbTreePickerModalData<unknown>, UmbTreePickerModalValue>(
|
||||
UMB_TREE_PICKER_MODAL_ALIAS,
|
||||
{
|
||||
modal: {
|
||||
|
||||
@@ -17,5 +17,5 @@ export * from './string/generate-umbraco-alias.function.js';
|
||||
export * from './string/increment-string.function.js';
|
||||
export * from './string/split-string-to-array.js';
|
||||
export * from './string/to-camel-case/to-camel-case.function.js';
|
||||
export type * from './type/deep-partial.type.js';
|
||||
export type * from './type/deep-partial-object.type.js';
|
||||
export type * from './type/diff.type.js';
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import type { DeepPartial } from '../type/deep-partial.type.js';
|
||||
import type { UmbDeepPartialObject } from '../type/deep-partial-object.type.js';
|
||||
|
||||
/**
|
||||
* Deep merge two objects.
|
||||
* @param target
|
||||
* @param ...sources
|
||||
*/
|
||||
export function umbDeepMerge<T extends { [key: string]: any }>(source: DeepPartial<T>, fallback: T) {
|
||||
export function umbDeepMerge<
|
||||
T extends { [key: string]: any },
|
||||
PartialType extends UmbDeepPartialObject<T> = UmbDeepPartialObject<T>,
|
||||
>(source: PartialType, fallback: T) {
|
||||
const result = { ...fallback };
|
||||
|
||||
for (const key in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, key) && source[key] !== undefined) {
|
||||
if (source[key].constructor === Object && fallback[key].constructor === Object) {
|
||||
result[key] = umbDeepMerge(source[key], fallback[key]);
|
||||
if (source[key]?.constructor === Object && fallback[key].constructor === Object) {
|
||||
result[key] = umbDeepMerge(source[key] as any, fallback[key]);
|
||||
} else {
|
||||
result[key] = source[key] as any;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*export type DeepPartial<T> = T extends { [key: string]: any }
|
||||
? {
|
||||
[P in keyof T]?: DeepPartial<T[P]>;
|
||||
}
|
||||
: T;
|
||||
*/
|
||||
|
||||
// Notice this can be way more complex, but in this case I just wanted to cover pure objects, to match our deep merge function [NL]
|
||||
// See https://stackoverflow.com/questions/61132262/typescript-deep-partial for more extensive solutions.
|
||||
/**
|
||||
* Deep partial object type, making objects and their properties optional, but only until a property of a different type is encountered.
|
||||
* This means if an object holds a property with an array that holds objects, the array will be made optional, but the properties of the objects inside the array will not be changed.
|
||||
* @type UmbDeepPartialObject
|
||||
* @generic T - The object to make partial.
|
||||
* @returns A type with all properties of objects made optional.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export type UmbDeepPartialObject<T> = T extends Function
|
||||
? T
|
||||
: // Thing extends Array<infer InferredArrayMember>
|
||||
// ? DeepPartialArray<InferredArrayMember> :
|
||||
T extends { [key: string]: any }
|
||||
? UmbDeepPartialObjectProperty<T>
|
||||
: T | undefined;
|
||||
|
||||
//interface DeepPartialArray<Thing> extends Array<DeepPartial<Thing>> {}
|
||||
|
||||
type UmbDeepPartialObjectProperty<Thing> = {
|
||||
[Key in keyof Thing]?: UmbDeepPartialObject<Thing[Key]>;
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
export type DeepPartial<T> = T extends object
|
||||
? {
|
||||
[P in keyof T]?: DeepPartial<T[P]>;
|
||||
}
|
||||
: T;
|
||||
Reference in New Issue
Block a user