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

Reply via email to