Skip to content

Commit

Permalink
add onCancel option (#14)
Browse files Browse the repository at this point in the history
* Add `onCancel` option to give a function to run when a Cancel action is triggered. fix #13
* Improve doc by fixing a few errors and adding default values and
  code examples
  • Loading branch information
NicolasCARPi authored Mar 25, 2024
1 parent 6216cb2 commit 6f714c4
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 48 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog for malle

## 2.6.0

* Add `onCancel` option to give a function to run when a Cancel action is triggered. fix #13

## 2.5.2

* Allow returning a `Promise<boolean>` with `onEdit`
Expand Down
18 changes: 18 additions & 0 deletions demo/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,24 @@ new Malle({
// instead of using listenNow, we call listen() right after instanciation
}).listen();

// onCancel
new Malle({
fun: value => {
return new Promise(resolve => resolve(value));
},
formClasses: ['d-inline-flex'],
debug: true,
onCancel: () => {
console.log('a cancel action has been detected');
return true;
},
onBlur: Action.Cancel,
cancel: 'Cancel',
cancelClasses: ['btn', 'btn-danger'],
listenOn: '.onCancel',
listenNow: true,
});

new Malle({
fun: value => {
return new Promise(resolve => resolve(value));
Expand Down
79 changes: 44 additions & 35 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,42 +15,51 @@
<body>

<div class='container-fluid'>
<h1>malle demo page</h1>
<h2>API documentation</h2>
<p>If you are looking for the complete API documentation, with all possible options and interfaces with their description, it is available here: <a href='/malle/api'>full API documentation</a>.</p>
<h2>Minimal example</h2>
<p>The text <span id='minimal' class='fw-bold'>in bold</span> is malleable. Try clicking on it.</p>
<h2>Selecting input type</h2>
<p class='tip'>Use <code>data-ma-type</code> attribute to select the appropriate input type, or set it in the options with <code>inputType</code>.</p>
<p><code>data-ma-type='text'</code><br>This is the default type and doesn't need to be specified. <span data-malleable='true'>Some malleable text.</span></p>
<p><code>data-ma-type='number'</code><br>Click on the number: <span data-malleable='true' data-ma-type='number'>3</span></p>
<p><code>data-ma-type='email'</code><br>Email example: <span data-malleable='true' data-ma-type='email'>[email protected]</span></p>
<p><code>data-ma-type='url'</code><br>Url example: <span data-malleable='true' data-ma-type='url'>https://www.deltablot.com</span></p>
<h3>Select example</h3>
<p>Select example. Here we set <code>inputType: InputType.Select</code> and <code>selectOptions: [{value: '1', text: 'Blah'}]</code> in the options.<br>Best country: <span class='malleableSelect'>France</span></p>
<h3>Textarea example</h3>
<p class='malleableTextarea'>This is a malleable paragraph that will transform into a textarea with action buttons. Settings are defined in JS options here.</p>
<h3>Datetime example</h3>
<p>Using <code>data-ma-type='datetime-local'</code>. Upcoming deadline: <span data-malleable='true' data-ma-type='datetime-local'>2022-07-14T13:37</span>.</p>
<div class='mb-5'>
<h1>malle demo page</h1>
<h2>API documentation</h2>
<p>If you are looking for the complete API documentation, with all possible options and interfaces with their description, it is available here:</p>
<a href='/malle/api'><button class='btn btn-primary'>Complete API documentation</button></a>
<h2>Minimal example</h2>
<p>The text <span id='minimal' class='fw-bold'>in bold</span> is malleable. Try clicking on it.</p>
<h2>Selecting input type</h2>
<p class='tip'>Use <code>data-ma-type</code> attribute to select the appropriate input type, or set it in the options with <code>inputType</code>.</p>
<p><code>data-ma-type='text'</code><br>This is the default type and doesn't need to be specified. <span data-malleable='true'>Some malleable text.</span></p>
<p><code>data-ma-type='number'</code><br>Click on the number: <span data-malleable='true' data-ma-type='number'>3</span></p>
<p><code>data-ma-type='email'</code><br>Email example: <span data-malleable='true' data-ma-type='email'>[email protected]</span></p>
<p><code>data-ma-type='url'</code><br>Url example: <span data-malleable='true' data-ma-type='url'>https://www.deltablot.com</span></p>
<h3>Select example</h3>
<p>Select example. Here we set <code>inputType: InputType.Select</code> and <code>selectOptions: [{value: '1', text: 'Blah'}]</code> in the options.<br>Best country: <span class='malleableSelect'>France</span></p>
<h3>Textarea example</h3>
<p class='malleableTextarea'>This is a malleable paragraph that will transform into a textarea with action buttons. Settings are defined in JS options here.</p>
<h3>Datetime example</h3>
<p>Using <code>data-ma-type='datetime-local'</code>. Upcoming deadline: <span data-malleable='true' data-ma-type='datetime-local'>2022-07-14T13:37</span>.</p>

<h2>Selecting behavior on blur</h2>
<p class='tip'>Use <code>data-ma-blur</code> attribute to select the appropriate behavior when user clicks outside the input, or set it in the options with <code>onBlur</code>.</p>
<p><code>data-ma-blur='cancel'</code><br><span data-malleable='true' data-ma-blur='cancel'>Clicking outside the input will cancel edition.</span></p>
<p><code>data-ma-blur='submit'</code><br><span data-malleable='true' data-ma-blur='submit'>Clicking outside the input will submit changes.</span></p>
<p><code>data-ma-blur='ignore'</code><br><span data-malleable='true' data-ma-blur='ignore'>Clicking outside the input will do nothing.</span></p>
<h2>Selecting behavior on Enter keypress</h2>
<p class='tip'>Use <code>data-ma-enter</code> attribute to select the appropriate behavior when user presses the Enter key, or set it in the options with <code>onEnter</code>.</p>
<p><code>data-ma-enter='cancel'</code><br><span data-malleable='true' data-ma-enter='cancel'>Pressing Enter will cancel edition.</span></p>
<p><code>data-ma-enter='submit'</code><br><span data-malleable='true' data-ma-enter='submit'>Pressing Enter will submit changes.</span></p>
<p><code>data-ma-enter='ignore'</code><br><span data-malleable='true' data-ma-enter='ignore'>Pressing Enter will do nothing.</span></p>
<h2>Setting behavior for both Blur action and Enter keypress</h2>
<p data-malleable='true' data-ma-enter='submit' data-ma-blur='submit'>This text will submit onBlur and also onEnter.</p>
<h3>Same with Escape keypress</h3>
<p class='tip'>The default behavior is to Cancel action, but here is an example to ignore an Escape keypress:</p>
<p><code>data-ma-escape='ignore'</code><br><span data-malleable='true' data-ma-escape='ignore'>Pressing Escape will do nothing.</span></p>
<p><code>data-ma-escape='cancel'</code><br><span data-malleable='true' data-ma-escape='cancel'>Pressing Escape will cancel edition (default behavior, attribute doesn't need to be added).</span></p>
<h2>Adding a placeholder</h2>
<p><code>data-ma-placeholder='[email protected]'</code><br>Your email is: <span data-malleable='true' data-ma-placeholder='[email protected]' data-ma-type='email'>[email protected]</span></p>
<h2>Selecting behavior on blur</h2>
<p class='tip'>Use <code>data-ma-blur</code> attribute to select the appropriate behavior when user clicks outside the input, or set it in the options with <code>onBlur</code>.</p>
<p><code>data-ma-blur='cancel'</code><br><span data-malleable='true' data-ma-blur='cancel'>Clicking outside the input will cancel edition.</span></p>
<p><code>data-ma-blur='submit'</code><br><span data-malleable='true' data-ma-blur='submit'>Clicking outside the input will submit changes.</span></p>
<p><code>data-ma-blur='ignore'</code><br><span data-malleable='true' data-ma-blur='ignore'>Clicking outside the input will do nothing.</span></p>
<h2>Selecting behavior on Enter keypress</h2>
<p class='tip'>Use <code>data-ma-enter</code> attribute to select the appropriate behavior when user presses the Enter key, or set it in the options with <code>onEnter</code>.</p>
<p><code>data-ma-enter='cancel'</code><br><span data-malleable='true' data-ma-enter='cancel'>Pressing Enter will cancel edition.</span></p>
<p><code>data-ma-enter='submit'</code><br><span data-malleable='true' data-ma-enter='submit'>Pressing Enter will submit changes.</span></p>
<p><code>data-ma-enter='ignore'</code><br><span data-malleable='true' data-ma-enter='ignore'>Pressing Enter will do nothing.</span></p>
<h2>Setting behavior for both Blur action and Enter keypress</h2>
<p data-malleable='true' data-ma-enter='submit' data-ma-blur='submit'>This text will submit onBlur and also onEnter.</p>
<h3>Same with Escape keypress</h3>
<p class='tip'>The default behavior is to Cancel action, but here is an example to ignore an Escape keypress:</p>
<p><code>data-ma-escape='ignore'</code><br><span data-malleable='true' data-ma-escape='ignore'>Pressing Escape will do nothing.</span></p>
<p><code>data-ma-escape='cancel'</code><br><span data-malleable='true' data-ma-escape='cancel'>Pressing Escape will cancel edition (default behavior, attribute doesn't need to be added).</span></p>
<h2>Adding a placeholder</h2>
<p><code>data-ma-placeholder='[email protected]'</code><br>Your email is: <span data-malleable='true' data-ma-placeholder='[email protected]' data-ma-type='email'>[email protected]</span></p>
<h2>Running a function when the Cancel button is pressed</h2>
<p>The <code>onCancel</code> function will run when an <code>Action.Cancel</code> action is triggered, like clicking the Cancel button. It must return <code>true</code> for the input to be reverted to the original element.
<a href='https://deltablot.github.io/malle/api/interfaces/Options.html#onCancel.onCancel-1' target='_blank'>See API documentation</a>
<br>
<p class='onCancel fw-bold'>click me and then click cancel while looking at the console output</p>
</p>
</div>
</div>

<script src='demo.js' type='module'></script>
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@deltablot/malle",
"version": "2.5.2",
"version": "2.6.0",
"description": "Make text elements malleable, without dependencies.",
"main": "dist/main.js",
"typings": "dist/main.d.ts",
Expand Down
103 changes: 93 additions & 10 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,50 +59,125 @@ export interface Options {
*/
before?(original: HTMLElement, event:Event): boolean;
// The text displayed on Cancel button.
// @example Abort
cancel?: string;
// The classes added to Cancel button.
// @example ['btn', 'btn-secondary']
cancelClasses?: Array<string>;
// The classes added to the form element.
formClasses?: Array<string>;
// The classes added to the input element.
inputClasses?: Array<string>,
// Enabling debug mode will produce verbose output in the console.
// @default false
debug?: boolean;
// This is where you define the type of event that will trigger malle.
// @default EventType.Click
event?: EventType;
// Should the newly created input grab focus?
inputType?: InputType;
// This is the user function that is called on submit.
// @default true
focus?: boolean;
// Define the type of the input element.
// The classes added to the form element.
// @example ['d-inline-flex']
formClasses?: Array<string>;
/**
* This is the main and only mandatory option parameter. It is the user function that is called when the Submit action happens.
* @example with a custom function
* // this is the user function that will process the new value
* // typically this will POST to some endpoint and get some json back
* // it receives the event
* const myCustomFunction = (value, orig) => {
* console.log(`New text: ${value}`);
* // do something with that value, like POSTing it somewhere
* return new Promise(resolve => resolve(value));
* };
*
* new Malle({
* fun: myCustomFunction,
* }).listen();
*/
fun(value: string, original: HTMLElement, event:Event, input: HTMLInputElement|HTMLSelectElement): Promise<string>;
// The classes added to the input element.
// @example ['form-control']
inputClasses?: Array<string>,
// Define the type of the input element.
// @default InputType.Text
inputType?: InputType;
// Start listening immediatly or not.
// @default false
listenNow?: boolean;
// HTML selector to target malleable elements on the page.
listenOn?: string;
// What Action should be taken when focus of the input is lost.
onBlur?: Action;
// This function runs right after the form is created.
/**
* A function that runs when a Cancel action is performed. Must return `true` or the input is not reverted to the original element.
* @example
* ```javascript
* onCancel: (original, event, input) => {
* console.log('a cancel action has been detected');
* return true;
* },
* ```
*/
onCancel?(original: HTMLElement, event:Event, input: HTMLInputElement|HTMLSelectElement): boolean | Promise<boolean>;
/**
* This function runs right after the form is created. Its return value has no impact.
* @example
* ```javascript
* onEdit: (original, event, input) => {
* console.log('this will run after the input is present on the page');
* return true;
* },
* ```
*/
onEdit?(original: HTMLElement, event:Event, input: HTMLInputElement|HTMLSelectElement): boolean | Promise<boolean>;
// What Action should be taken when the Enter key is pressed?
// @default Action.Submit
onEnter?: Action;
// What Action should be taken when the Escape key is pressed?
// @default Action.Cancel
onEscape?: Action;
// A text that is shown on empty input.
placeholder?: string;
// Do nothing if new value is the same as the old value.
// @default true
requireDiff?: boolean;
// Use innerHTML instead of innerText (only use if the return value is trusted HTML).
// @default false
returnedValueIsTrustedHtml?: boolean;
// An array of options for InputType.Select. Can also be a Promise and fetched asynchronously.
/*
* An array of options for InputType.Select. Can also be a Promise and fetched asynchronously.
* @example Directly give the options to use
* ```javascript
* selectOptions: [
* { value: '1', text: 'Rivoli' },
* { value: '2', text: 'Austerlitz' },
* { value: '3', text: 'Marengo', selected: true },
* ],
* ```
* @example Fetch the options with an HTTP request or any other function
* ```javascript
* // Change the keys used to lookup value and text
* selectOptionsValueKey: 'id',
* selectOptionsTextKey: 'title',
* // this promises to return an Array with objects that have the keys "id" and "title"
* selectOptions: Something.getOptions(),
* ```
*/
selectOptions?: Array<SelectOptions> | Promise<Array<SelectOptions>>;
// What is the name of the key to use to lookup the values in the selectOptions array?
// @default value
selectOptionsValueKey?: string;
// What is the name of the key to use to lookup the option text in the selectOptions array?
// @default text
selectOptionsTextKey?: string;
// The text on the Submit button.
// @example Save changes
submit?: string;
// The classes added to the submit button.
/**
* The classes added to the submit button.
* @example With bootstrap classes
* ```
* submitClasses: ['btn', 'btn-primary', 'mt-2'],
* ```
*/
submitClasses?: Array<string>;
// The text added on hover of the malleable element. Uses the `title` attribute.
tooltip?: string;
Expand Down Expand Up @@ -145,6 +220,7 @@ export class Malle {
listenNow: false,
listenOn: '[data-malleable="true"]',
onBlur: Action.Submit,
onCancel: undefined,
onEdit: undefined,
onEnter: Action.Submit,
onEscape: Action.Cancel,
Expand Down Expand Up @@ -230,6 +306,13 @@ export class Malle {
cancel(event: Event): boolean {
event.preventDefault();
this.debug(event.toString());
// execute the before hook
if (typeof this.opt.onCancel === 'function') {
this.debug('running onCancel function');
if (this.opt.onCancel(this.original, event, this.input) !== true) {
return;
}
}
this.debug('reverting to original element');
this.form.replaceWith(this.original);
return true;
Expand Down

0 comments on commit 6f714c4

Please sign in to comment.