Skip to content

Commit

Permalink
add decorator allowedAction to the callable Button actions
Browse files Browse the repository at this point in the history
  • Loading branch information
jrief committed Jun 12, 2024
1 parent 31ab482 commit 2b6480b
Showing 1 changed file with 44 additions and 24 deletions.
68 changes: 44 additions & 24 deletions client/django-formset/DjangoFormset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,24 @@ class TernaryAction {
}


/*
* Decorator to be added to functions in class DjangoButton which are eligible to be chained into an action queue.
*/
function allowedAction(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
const buttonAction = originalMethod.apply(this, args);
if (typeof buttonAction !== 'function')
throw new Error(`DjangoButton.${propertyKey} is not a valid action.`);
return (...funcArgs: any[]) => buttonAction.apply(target, funcArgs);
};

descriptor.value.isAllowedAction = true; // tag function to be allowed as ButtonAction
return descriptor;
}



class DjangoButton {
private readonly formset: DjangoFormset;
public readonly element: HTMLButtonElement;
Expand Down Expand Up @@ -593,7 +611,6 @@ class DjangoButton {
/**
* Event handler to be called when someone clicks on the button.
*/
// @ts-ignore
private clicked = (event: Event) => {
if (event.currentTarget === this.element) {
this.formset.currentActiveButton = this;
Expand All @@ -610,7 +627,7 @@ class DjangoButton {
/**
* Disable the button for further submission.
*/
// @ts-ignore
@allowedAction
private disable() {
return (response: Response) => {
this.element.disabled = true;
Expand All @@ -621,7 +638,7 @@ class DjangoButton {
/**
* Re-enable the button for further submission.
*/
// @ts-ignore
@allowedAction
private enable() {
return (response: Response) => {
this.element.disabled = false;
Expand All @@ -632,7 +649,7 @@ class DjangoButton {
/**
* Validate form content and submit to the endpoint given in element `<django-formset>`.
*/
// @ts-ignore
@allowedAction
submit(data?: Object) {
return () => {
return new Promise((resolve, reject) => {
Expand All @@ -646,7 +663,7 @@ class DjangoButton {
/**
* Validate the current form content and only submit that form's content to the endpoint given in element `<django-formset>`.
*/
// @ts-ignore
@allowedAction
submitPartial(data?: Object) {
return () => {
return new Promise((resolve, reject) => {
Expand All @@ -661,15 +678,15 @@ class DjangoButton {
/**
* Reset form content to their initial values.
*/
// @ts-ignore
@allowedAction
reset() {
return (response: Response) => {
this.formset.resetToInitial();
return Promise.resolve(response);
};
}

// @ts-ignore
@allowedAction
private reload(includeQuery?: Boolean) {
return (response: Response) => {
includeQuery ? location.reload() : location.replace(window.location.pathname);
Expand All @@ -687,7 +704,7 @@ class DjangoButton {
* @param proceedUrl (optional): If set, proceed to that URL regardless of the
* response status.
*/
// @ts-ignore
@allowedAction
private proceed(proceedUrl?: string) {
return async (response: Response) => {
if (typeof proceedUrl === 'string' && proceedUrl.length > 0) {
Expand All @@ -711,7 +728,7 @@ class DjangoButton {
*
* @param ms: Time to wait in milliseconds.
*/
// @ts-ignore
@allowedAction
private delay(ms: number) {
return (response: Response) => new Promise(resolve => this.timeoutHandler = window.setTimeout(() => {
this.timeoutHandler = undefined;
Expand All @@ -722,7 +739,7 @@ class DjangoButton {
/**
* Replace the button's decorator against a spinner icon.
*/
// @ts-ignore
@allowedAction
private spinner() {
return (response: Response) => {
this.decoratorElement?.replaceChildren(this.spinnerElement);
Expand All @@ -733,15 +750,15 @@ class DjangoButton {
/**
* Replace the button's decorator against an okay animation.
*/
// @ts-ignore
@allowedAction
private okay(ms?: number) {
return this.decorate(this.okayElement, ms);
}

/**
* Replace the button's decorator against a bummer animation.
*/
// @ts-ignore
@allowedAction
private bummer(ms?: number) {
return this.decorate(this.bummerElement, ms);
}
Expand All @@ -751,7 +768,7 @@ class DjangoButton {
*
* @param cssClass: The CSS class.
*/
// @ts-ignore
@allowedAction
private addClass(cssClass: string) {
return (response: Response) => {
this.element.classList.add(cssClass);
Expand All @@ -764,7 +781,7 @@ class DjangoButton {
*
* @param cssClass: The CSS class.
*/
// @ts-ignore
@allowedAction
private removeClass(cssClass: string) {
return (response: Response) => {
this.element.classList.remove(cssClass);
Expand All @@ -777,7 +794,7 @@ class DjangoButton {
*
* @param cssClass: The CSS class.
*/
// @ts-ignore
@allowedAction
private toggleClass(cssClass: string) {
return (response: Response) => {
this.element.classList.toggle(cssClass);
Expand All @@ -790,7 +807,7 @@ class DjangoButton {
*
* @param event: The named event.
*/
// @ts-ignore
@allowedAction
private emit(namedEvent: string, detail?: Object) {
return (response: Response) => {
const options = {bubbles: true, cancelable: true};
Expand All @@ -808,7 +825,7 @@ class DjangoButton {
* For debugging purpose only: Intercept, log and forward the response object to the next handler.
* @param selector: If selector points onto a valid element in the DOM, the server response is inserted.
*/
// @ts-ignore
@allowedAction
private intercept(selector?: string) {
return (response: Response) => {
const body = {
Expand All @@ -831,7 +848,7 @@ class DjangoButton {
/**
* Clear all errors in the current django-formset.
*/
// @ts-ignore
@allowedAction
private clearErrors() {
return (response: Response) => {
this.formset.clearErrors();
Expand All @@ -842,7 +859,7 @@ class DjangoButton {
/**
* Scroll to first element reporting an error.
*/
// @ts-ignore
@allowedAction
private scrollToError() {
return (response: Response) => {
const errorReportElement = this.formset.findFirstErrorReport();
Expand All @@ -856,7 +873,7 @@ class DjangoButton {
/**
* Confirm a user response. If it is accepted proceed, otherwise reject.
*/
// @ts-ignore
@allowedAction
private confirm(message: string) {
if (typeof message !== 'string')
throw new Error("The confirm() action requires a message.")
Expand All @@ -873,7 +890,7 @@ class DjangoButton {
* Show an alert message with the response text for other types of errors, such as permission denied.
* This can be useful information to the end user in case the Django endpoint can not process a request.
*/
// @ts-ignore
@allowedAction
private alertOnError() {
return (response: Response) => {
if (response.status !== 422) {
Expand All @@ -884,8 +901,9 @@ class DjangoButton {
}

/**
* Action to activate a button so that it can be used by dialogs.
* Action to activate a button so that a dialog can be induced by it.
*/
@allowedAction
private activate(...args: any[]) {
return (response: Response) => {
this.formset.updateOperability(...args);
Expand All @@ -896,13 +914,15 @@ class DjangoButton {
/**
* Transfer value from one element to another one.
*/
@allowedAction
private setFieldValue(target: Path, source: FieldValue) {
return (response: Response) => {
this.formset.setFieldValue(target, source);
return Promise.resolve(response);
}
}

@allowedAction
private deletePartial(target: Path, source: FieldValue) {
return (response: Response) => {
if (typeof source === 'string' && parseInt(source)) {
Expand All @@ -915,13 +935,13 @@ class DjangoButton {
/**
* Dummy action to be called in case of empty actionsQueue.
*/
@allowedAction
private noop() {
return (response: Response) => {
return Promise.resolve(response);
}
}

// @ts-ignore
/*
* Called after all actions have been executed.
*/
Expand Down Expand Up @@ -1010,7 +1030,7 @@ class DjangoButton {
const innerAction = (action: any) => {
if (isPlainObject(action) && typeof action.funcname === 'string' && Array.isArray(action.args)) {
const func = this[action.funcname as keyof DjangoButton];
if (typeof func !== 'function')
if (typeof func !== 'function' || !((func as any)['isAllowedAction']))
throw new Error(`Unknown function '${action.funcname}'.`);
return new ButtonAction(func, action.args.map(innerAction));
}
Expand Down

0 comments on commit 2b6480b

Please sign in to comment.