pwix:modal
What is it ?
A Bootstrap-based Meteor package which provides draggable and resizable modal dialogs.
Installation
As simple as:
meteor add pwix:modal meteor npm install lodash --save
Usage
Just call
import { Modal } from 'meteor/pwix:modal'; Modal.run( parms )
and you're done!
Without any parameters, the displayed modal will have a header with a dismiss button but no title, an empty body, and a footer with a single OK button.
See below for the available parameters.
Opening several modals
Though the Bootstrap documentation prevents against it, this package let you open more than only one modal at a time. Each is stacked on top of the previous one, and take the focus while it is active and no modal is opened on top of it.
Configuring
The package's behavior can be configured through a call to the Modal.configure() method, with just a single javascript object argument, which itself should only contains the options you want override.
Known configuration options are:
-
backdropOpacityThe opacity of the backdrop when it is visible.
Default to
0.5.Since v2.5.
-
backdropVisibleWhether the backdrop is visible.
Default to
falsefor compatibility reason:-
bootstrap's backdrop defaults to be visible -
pwix:modalhas initially chosen to not display this backdrop, thus the reason of the default value of this parameter.
Since v2.5.
-
-
closeByBackdropWhether the dialogs can be closed when clicking on the backdrop. This acts as the default value for
Modal.run()arguments.Default to
true. -
closeBykeyboardWhether the dialogs can be closed when clicking on 'Escape' key. This acts as the default value for
Modal.run()arguments.Default to
true. -
contentClassesArrayAn optional array of classes to be added to the '.modal-content' component. Modal will successively apply each item of the array as modal dialogs stack.
Default to none.
-
verbosityThe verbosity level as:
Modal.C.Verbose.NONE
or an OR-ed value of integer constants:
-
Modal.C.Verbose.CONFIGURETrace configuration operations.
This is the default.
-
Modal.C.Verbose.FOCUSTrace the focus() function.
-
Modal.C.Verbose.NOMODALTrace the modal research when there is none.
-
Modal.C.Verbose.RESIZINGTrace resizing informations.
-
Modal.C.Verbose.STACKTrace push into and pop from stack.
Defaults to
Modal.C.Verbose.NONE.
Remind too that Meteor packages are instanciated at application level. They are so only configurable once, or, in other words, only one instance has to be or can be configured. Addtionnal calls to Modal.configure() will just override the previous one. You have been warned: only the application should configure a package.
What does it provide ?
Modal
The globally exported object.
Classes
mdModal
The class which manages each modal dialog. It has following public methods:
-
askClose()Let the application a chance to prevent the close of the modal.
If a
beforeClose()function has been configured, it is expected to return aPromisewhich must resolve to:trueto let the modal be closedfalseto prevent the close.
-
buttonFind( button<String>): <jQuery|null>Find in the modal the identified button.
Because this method makes a search on the
data-md-btn-idattribute, it may be irrelevant when a specific footer has been defined. -
close()Close the modal.
-
focus( opts<Object|undefined>)Set the focus on the first inputable field or the last button.
optsis an optional options object with following keys:field: the target field selector, defaulting to the first inputable field of the body, or the last button.
The application may have its own opinion about which is the first inputable field, or, in other words, which field should be the default when the user presses a key. It will most probably to set the focus from its own
onRendered()function. But this later is triggered before ours. So, because we are executed last, we cannot provide any suitable default.As a consequence, each client application shoud call this function itself if it wants the user has a better default than the one provided.
-
set( arg<Object|undefined>)Set the specified properties on the modal.
argmust be a Javascript object with following keys:-
id: the identifier of the to-be-configured modal, defaulting to the current topmost -
autoFocus: when specified, whether the focus is automatically managed by the modal itself.See the
mdAutoFocusparameter to get a full description. -
beforeClose: when specified, the function to be called by the modal to get an authorization to close.See the
mdBeforeCloseparameter to get a description of the function. -
body: when specified, the name of the Blaze template to be set as the modal body -
bodyHeight: when specified, set a minimal new body heightBody height may be specified with a
'+'or'-'prefix to respectively increase or decrease the current body height. -
buttons: when specified, a string, an array of strigs, an object or an array of object, each one providing the properties to be set on a button, as:id: mandatory, defaulting to the string itself if only a string is providedlabelclassesenablednametypehtmlcbdismissifExist: only apply if the button already exists, defaulting to false; this means that, if you do not specify this attribute, you may create a new button!
If a button has not been previously defined, then it is added at the end of the list.
This is also the case when the buttons are only specified as strings, not objects. In that case, we consider that this is a request to add a new button, which must not exist yet.
-
classes: when specified, classes to be added to the '.modal' element -
classesBody: when specified, classes to be added to the '.modal-body' element -
classesContent: when specified, classes to be added to the '.modal-content' element -
classesFooter: when specified, classes to be added to the '.modal-footer' element -
classesHeader: when specified, classes to be added to the '.modal-header' element -
closeByBackdrop: when specified, whether the dialog should be closed when clicking on the backdrop -
closeByHeader: when specified, whether the header exhibits a dismiss button -
closeByKeyboard: when specified, whether the dialog should be closed when hitting Escape -
footer: when specified, the name of the Blaze template to be set as the modal footerJust set to
nullto pass from a specific footer to the standard one.Specifying a particular footer takes precedence over the standard one.
When a particular footer is specified, then the button methods are no more operationnal, and you have to manage them yourself.
-
fullscreen: when specified, whether the dialog should be displayed in full screen mode -
moveTop: when specified, whether the modal should be moved vertically. -
target: when specified, the JQuery object which must receive events for that modalThis method is usually called from the rendered body template
onRendered()function. At that time, not only the DOM is rendered for this element, but it is very probable that this is in this template that the triggered events will be useful. -
title: when specified, the title of the modal
-
-
target(): <jQuery>Returns the current target of modal events.
Methods
The life of the modal
-
Modal.run({ parms })Creates and shows a modal dialog with
parmsparameters object. Known parameters are:-
mdAutoFocusWhether we should try to initialize the focus ourselves,or let the application manage it.
When
true, we try to set the focus on the first inputable field of the body, or the last button of the footer.Though this default aims to make the package easier to use, the application should be conscious that it also prevents the application to put the focus itself.
Defaults to
true. -
mdBeforeCloseA function to be called when the user asks for close the modal, by clicking on the backdrop, or the close button of the header, or a close button in the footer. This function let the application allows or forbid the close:
- the function takes a unique argument, which is the modal identifier
- it is expected that the function returns a
Promisewhich must resolve to:trueto let the modal be closedfalseto prevent the modal to be closed.
The default is to leave the modal be closed when the user asks for that.
-
mdBodyThe name of a Blaze template to be rendered as the dialog body.
No default.
-
mdButtonsThe buttons to be displayed in the standard footer, as a string, an array of strings, an object or an array of objects.
Only considered if a specific footer is not asked (see
mdFooterparm).Default is to have one
OKbutton.When provided as an array, the buttons are displayed from left (the first element of the array) to right (the last element).
See also Buttons management for the syntax of this data.
-
mdClassesA string which contains the classes to be added to the '
.modal' element.No default.
-
mdClassesBodyA string which contains the classes to be added to the '
.modal-body' element.No default.
-
mdClassesContentA string which contains the classes to be added to the '
.modal-content' element.No default.
-
mdClassesFooterA string which contains the classes to be added to the '
.modal-footer' element.No default.
-
mdClassesHeaderA string which contains the classes to be added to the '
.modal-header' element.No default.
-
mdCloseByBackdropWhether clicking outside of the dialog should close it.
Defaults to configured value.
Note that you should keep this default if you plan to ask a confirmation to your user when he/she clicks on the backdrop.
-
mdCloseByHeaderWhether the header holds a
Closebutton.Defaults to
true.Note that you should keep this default if you plan to ask a confirmation to your user when he/she clicks on the cross check of the header.
-
mdCloseByKeyboardWhether
Escapekey closes the modal.Defaults to
true.Note that you should keep this default if you plan to ask a confirmation to your user when he/she hits the 'Escape' key.
-
mdFooterThe name of a Blaze template to be rendered as the dialog footer.
Default is to render a standard footer with at least one
OKbutton.If both are specified,
mdFootertakes precedence onmdButtons. -
mdFullScreenWhether the modal should be displayed in full screen mode.
This is nonetheless a rather bad idea in XS and S devices where the pagination should rather be reviewed.
And also a bad idea on larger displays, as this lead to very too big dialogs.
Reserve this use to dedicated less-than-MD devices.
-
mdHeightThe desired height of the modal content as a px-measure float.
Defaults to be left to Bootstrap automatism.
-
mdMoveTopWhether the modal should be moved vertically.
The value must be specified in pixel units, and can be negative (up-move) or positive (down-move).
-
mdPositionOverrides the default positioning with an OR-ed value of
Modal.C.Positionconstants. -
mdSizeKeyThe string name of the
localStorageitem which will record the last used width and height.No default.
Using this feature requires the user has accepted the use of functional cookies. The size will be stored as a
localStorageitem. -
mdTargetThe target of the events as a jQuery object.
Default is let bubble the events.
Note that at the time of the modal creation, you are not yet able to set the rendered template as the events target (as it has not yet been rendered). See also
Modal.target().Note also that the modal will be attached to the
bodyof the page. Events will so bubble directly from the modal to the body. -
mdTitleThe title of the dialog.
No default.
This method returns a string which is the unique identifier of the new modal.
-
-
Modal.count()Returns the count of opened modals.
-
Modal.topmost()Returns the current topmost modal dialog.
Buttons management
When using the standard footer, buttons can be specified either as an object or an array of objects.
An accepted object is a full object definition, with keys:
-
id: the button identifier as a string, mandatoryIt may be one of our known button identifiers as defined by the constants, or an identifier provided by the caller.
The special
Modal.C.ButtonExt.RESETidentifier let the application remove all previously defined buttons. -
label: the label of the buttonIf the button identifier is one of ours, then label defaults to the standard (localized) label associated with this button.
If the button identifier is provided by the caller, then label defaults to the identifier itself.
-
classes: the classes to be set for the buttonDefaults to:
btn-secondaryfor all but the last buttonsbtn-primaryfor the last (the rightest) button.
-
enabled: whether the button defaults to be enabled.Defaults to
true. -
name: the name of the buttonDefaults to button identifier.
-
type: the type of the buttonAccepted values are:
submitresetbutton
Defaults to
button.Please also note that a
type="submit"button will reload the page when activated. This is probably not what you want in a Meteor application. -
html: the full button definition<button>...</button>as a HTML stringNo default.
If this attribute is set, it takes precedence other
label,classes,enabled,nameandtype. -
cb: a function(modal_id, button_id)to be called when the button is clickedThe return value of the function is ignored.
-
dismiss: a boolean value which says whether clicking on the button should dismiss the modalDefaults to
trueif there is one single button, or if they areCANCELorCLOSEbuttons. Default tofalsein every other case.
All others parameters passed when creating the button are kept, and made available in button.parms data passed with md-click event.
Each button, apart those generated directly via the html key, has a date-md-btn-id attribute set to the button identifier.
Please note that all that buttons management is NOT relevant when using a specific footer.
Modal closing
Bootstrap let the modal be closed:
- by clicking on the backdrop, outside of the modal,
- by clicking on the header cross check,
- by hitting the 'Escape' key.
You can also add, e.g., a 'Cancel' button.
The three Bootstrap closing modes all depend of the configuration of the modal, i.e. whether closeByBackdrop, closeByHeader or closeByKeyboard are true.
When false, this closing mode is just disabled.
When one is true, or the user has clicked on your 'Cancel' button, then only pwix:modal tries to honor the beforeClose parameter which is expected to be an async function which will eventually return true or false.
Modal positionning
pwix:modal does its best to automatically position the new modal. Default is to stack on the last opened modal, slightly shifted to bottom and right. This is said 'AUTO' positionning.
The caller can ask to position the new modal in particular places:
-
horizontally and/or vertically centered in the screen (ak in the viewport),
-
horizontally and/or vertically centered relatively to the previous modal.
This request for a particular positioning can be specified when calling Modal.run() with the mdPosition parameter.
Translations
-
Modal.i18n.namespace()Returns the i18n namespace of the package.
Constants
Buttons
Modal.C.Button.OKModal.C.Button.CANCELModal.C.Button.CLOSEModal.C.Button.SAVEModal.C.Button.YESModal.C.Button.NO
Special identifiers
Modal.C.ButtonExt.RESET
These are our known, standard, button identifiers. Their labels are localizable.
Events
-
md-clickA button has been clicked.
The event holds a data object with:
-
id: the modal identifier -
button: the button properties with:id: the button identifier (always set)parms: the parameters initially passed when creating the button
-
parms: the parameters initialy passed toModal.run().
If the button holds a truthy
dismissproperty, or is the only button of the standard footer, then the dialog is closed. In other cases, it is the responsability of the event receiver to close the modal. -
-
md-closeAn event sent when the modal is about to close, whatever be the reason.
The event holds a data object with:
id: the modal identifierparms: the parameters initialy passed toModal.run().
Note that this event is only for information. It does not let the receiver to prevent the modal closing. In order to do that, see the
mdBeforeCloseparameter. -
md-readyThe modal has been rendered, the DOM is ready.
The event holds a data object with:
id: the modal identifierparms: the parameters initialy passed toModal.run().
Example
Say you have a template you want render in a modal:
1 <template name="my_panel"> 2 <div class="my-panel"> 3 4 <form> 5 <label for="" class="form-label form-label-sm frs-one">{{ i18n label="title_label" }}</label> 6 <input type="text" class="form-control form-control-sm frs-title" placeholder="{{ i18n label="title_placeholder" }}" value="{{ catTitle }}" /> 7 8 <label for="" class="form-label form-label-sm frs-one">{{ i18n label="description_label" }}</label> 9 <textarea class="form-control form-control-sm frs-description" placeholder="{{ i18n label="description_placeholder" }}" rows="3">{{ catDescription }}</textarea> 10 </form> 11 12 </div> 13 </template>
From the parent who mades the open decision, just run:
1 Modal.run({ 2 mdBody: 'my_panel', 3 mdTitle: 'A simple form', 4 mdButtons: [ Modal.C.Button.CANCEL, Modal.C.Button.SAVE ] 5 });
In the template JS:
1 Template.my_panel.onRendered( function(){ 2 Modal.setTarget( this.$( '.my-panel' )); 3 }); 4 5 ... 6 7 Template.my_panel.events({ 8 'md-click .my-panel'( event, instance, data ){ 9 if( data.button === Modal.C.Button.SAVE ){ 10 // do something 11 Modal.close(); 12 } 13 } 14 });
Modal attachment in the DOM
pwix:modal attaches its modals to the document body.
If you do not set a target, the events will eventually bubble until the body DOM element.
NPM peer dependencies
Starting with v 1.0.0, and in accordance with advices from the Meteor Guide, we no more hardcode NPM dependencies in the Npm.depends clause of the package.js.
Instead we check npm versions of installed packages at runtime, on server startup, in development environment.
Dependencies as of v 2.5.0:
1 'lodash': '^4.17.0'
Each of these dependencies should be installed at application level:
meteor npm install <package> --save
Translations
New and updated translations are willingly accepted, and more than welcome. Just be kind enough to submit a PR on the Github repository.
Cookies and comparable technologies
pwix:modal may use localStorage to record the size of a dialog through the mdSizeKey argument of the Modal.run() method.
Because this is dynamically done on a per dialog basis, and only on the caller request, the package cannot advertise of this use, relying on the caller own declaration.
P. Wieser
- Last updated on 2026, Apr. 2nd