Looks cool! Quick note: it looks like keyboard navigation with 'tab' can still take you to other text-entry fields etc in the background page and let you interact with them on the keyboard while the modal's open.
Should these also open as a full-screen overlay style on small-screen mobile devices (phones)? If so they may need to be fixed to handle scrolling through anything that's taller than a couple lines of text, as on small phones there's very little room once the keyboard's open. (And as a general note for Flow UI, "Hide" is very ambiguous; does it hide for just me or for everyone? Who is going to read the text I'm entering?) -- brion On Mon, Oct 20, 2014 at 9:16 AM, Shahyar Ghobadpour < [email protected]> wrote: > Hey everyone, > > We have developed a modal component for Flow, based on the design spec for > MediaWiki-UI modals/dialogs. Thus, we have named it *mw.Modal*. > > A very basic and feature-incomplete version of this modal can be seen at: > http://en.wikipedia.beta.wmflabs.org/wiki/Talk:Flow (hover over ellipsis > icon in topic, click "hide") > > This modal implements the design outlined here > <https://trello.com/c/OLnPx7M4/331-g-11-spike-build-modal-dialog-box-for-hide-delete-suppress>, > but automates a lot of the visual aspects of it based on the input you give > it. > > Below is an almost-verbatim copy of the original email I sent to another > mailing list, (discussion seen here: > https://lists.wikimedia.org/mailman/private/e2/2014-September/002494.html > ), detailing the general concepts and use: > > > *First, an example using event binding:* > >> // Instantiate mw.Modal >> *var* modal = new mw.*Modal*(); >> // Open up a modal with HTML >> modal.*open*( "<button>Bye!</button>" ); >> // Bubble click to wrapper, use it to close the parent modal of the target >> $( modal.*getContentNode*() ).*bind*( 'click', 'button', function () { >> mw.Modal.*close*( this ); } ); > > > > *Visuals (markup and CSS):* > As it stands, you can see the current designs for modals here > <https://trello.com/c/OLnPx7M4/331-g-11-spike-build-modal-dialog-box-for-hide-delete-suppress>, > with a screenshot of a Flow use case. This is being done with some very > simple markup: > > flow-ui-modal > > flow-ui-modal-layout > > flow-ui-modal-heading* (optional)* > > content + quick navigators* (automatic)* > > flow-ui-modal-content > > content > > > The CSS for this works back to IE6. It automatically centers on the > viewport, and scales up to max the viewport width/height, at which point > the modal would start showing scrollbar(s). No JS is needed for any of this. > > *Automatic enhancement:* > > - Shows title at top if it exists. > - Shows X on left of title to close if title exists. > - Shows ✓ form submission shortcut if title exists, and form with a > single primary button exists in content. > > > Caveat: This does not support off-center placement of modals (eg. placing > a modal by a specific element). For those use cases, I imagine > *mw-ui-tooltip* would be the solution, and it was written with that in > mind. > > > *Use (JavaScript):* > A constructor would be called (modal = new mw.Modal( [ String name, ] [ > Object settings ] )), which would give you access to control the flow of > your modal. > name may be omitted, but is simply there so you can easily determine > which modal is currently active with mw.Modal.getModal. > settings can have the following keys: open (same as modal.open), and title > . > > 1. modal.*open*( [ Array|Object|Element|jQuery|String contents ] ) - > You can visually render the modal using this method. Accepts > Element/jQuery, or HTML String. Returns mw.Modal instance. > - Multi-step modals with an Array. You can pass [ Element, Element ] > to have two steps. > - Multi-step modals with an Object to have *named step keys*. Pass { > *steps*: [ 'first', 'second', 'foobar' ], *first*: Element, *second*: > Element, *foobar*: Element } for three steps. > > 2. modal.*close*() - Closes and destroys the given instance of > mw.Modal. > - *(Global) *mw.Modal.close( [ Element node ] ) - Closes the > currently-open modal (no args), or the modal in which node belongs. > Returns false if none, true if successfully destroyed. > > 3. modal.*go*( int|String to ) - For a multi-step modal, goes to a > specific step by number or name. Returns false if invalid step, > mw.Modal instance otherwise. > modal.*next*() and > modal.*prev*() - For a multi-step modal, goes to the previous or next > step, if any are left. Returns false if none, mw.Modal instance > otherwise. > - *(Global) *mw.Modal.*go*( int|String to, [ Element node ] ) - Go > to step on the currently-open modal (no args), or the modal in which > node belongs. Returns false if none, mw.Modal instance otherwise. > - *(Global) *mw.Modal.*next*( [ Element node ] ) - Next/prev step > on the currently-open modal (no args), or the modal in which node > belongs. Returns false if none, mw.Modal instance otherwise. > > 4. modal.*setTitle*( String title ) - Changes the title of the > modal. > > 5. modal.*addSteps*( Array|Object|Element|jQuery|String contents ) - > Adds one or more steps, using the same arguments as modal.open. May > overwrite steps if any exist with the same key in Object mode. Returns > mw.Modal instance. > > 6. modal.*setStep*( int|String to, Element|jQuery|String contents ) - > Changes a given step. If String to does not exist in the list of > steps, throws an exception; int to always succeeds. If the given step > is the currently-active one, rerenders the modal contents. Returns mw.Modal > instance. > *Theoretically, you could use setStep to keep changing step 1 to > create a pseudo-multi-step modal.* > > 7. modal.*getSteps*() - Returns an Object with steps, and their > contents, eg. { *steps*: [ 1, 'foo', 'bar', 4 ], *1*: Element, '*foo*': > Element, '*bar*': Element, *4*: Element } > - *(Global)* mw.Modal.*getSteps*( [ Element node ] ) - Get an Array > of steps on the currently-open modal (no args), or the modal in which > node belongs. False on failure. > > 8. modal.*getNode*() - Returns the modal's wrapper Element, which > contains the header node and content node. > modal.*getContentNode*() - Returns the wrapping Element on which you > can bind bubbling events for your content. > - *(Global)* mw.Modal.*getContentNode*( [ Element node ] ) - Returns > the wrapping content Element on the currently-open modal (no args), or > the > modal in which node belongs. False on failure. > > 9. modal.*setInteractiveHandler*( String name, Function callback ) - > See "inline event handlers" below. Some helper handlers for modals are > predefined, including "modalClose", "modalNavigate" (with > *data-mwui-modal-to* attribute), "modalPrev", and "modalNext". > > An additional attribute, *data-mwui-interactive-target* is a jQuery > selector which allows you to point that event to interact with another > element, without having to put this selector in your JS. It supports an > unorthodox selector, the "closest parent" selector: "<". > eg. *data-mwui-interactive-target="< .foo .bar"* will find the closest > parent .foo, and then any nodes within .bar. > > 10. *(Global) *mw.Modal.*getModal*() - Returns an mw.Modal instance if > one is active, false otherwise. > > 11. modal.*getName*() - Returns the modal's name, as defined in > initialization. > > > *mw.Modal events:* > mw.Modal also inherits *ooJS' EventEmitter*. So, you may bind to > individual instances of mw.Modal, or even the global mw.Modal class itself. > See > wiki <http://www.mediawiki.org/wiki/OOjs/Events>. Current events list: > *open*(mw.Modal instance), *close*(mw.Modal instance, Element target), > *navigate*(mw.Modal instance, String nextStepKey), *render*(mw.Modal > instance) (only triggered on the first render of a given piece of > content, not subsequent ones). Returning *false* from any of these event > callbacks will cancel the event from continuing. > > > *Inline event callers:* > You can use special *data-* attributes to trigger/listen for events > without having to specifically bind to those elements. This allows you to > write more dynamic HTML, without having to adjust JavaScript when you > change elements or selectors. Some predefined helper handlers already exist > (see #8 above). > > *Example:* > > <button *data-mwui-interactive-handler*="*modalClose*">Close</button> >> > > These event handling techniques are borrowed from the way Flow currently > "binds" events via markup. We do this, because we found a significant > amount of code was dedicated to finding elements with selectors in JS. Any > changes to markup required a change to the JS as well. This method allows > us to change the markup without changing the JS in many cases, as the > selectors are localized to the HTML. In addition, cumbersome parent > selecting is handled with our own "closest" selector (<). > > In addition, we've been experimenting with an event forwarding mechanism > which "forwards" mouse & keyboard events from within the modal, out to the > element which triggered the modal to begin with (or any other arbitrary > node). This allows us to capture events within the modal, without having to > bind directly to it. > > > --Shahyar > > > _______________________________________________ > Design mailing list > [email protected] > https://lists.wikimedia.org/mailman/listinfo/design > >
_______________________________________________ Design mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/design
