OOUI

OOjs UI (Object-Oriented JavaScript – User Interface) is a modern JavaScript UI toolkit for browsers. It's a set of classes that you can use, that also let you compose more complex widgets.

It drives the dialogs, toolbars, and much of the other chrome of the VisualEditor.

MediaWiki client code using it will benefit from UX standardization.

Dependencies
OOjs UI is a separate project,. Grunt tasks are used to build the distributable library files (for more details see the project ). Its nifty interactive demo uses this library, and VisualEditor incorporates the library in.

It has a lightweight classing model for UI elements, which is built on the class and event system of OOjs.

At the moment OOjs UI is provided as a single module (unlike jQuery UI which is more modularised, with independent plugins). To use it in a MediaWiki project add  to the dependencies of your ResourceLoader module.

The only external library OOjs UI depends on is jQuery. It doesn't assume or depend on a MediaWiki environment (not even MediaWiki localization or ResourceLoader).

OOjs UI has a lightweight default theme with miinimal CSS styles and a small icon set. Other themes build on top of this. One day, MediaWiki will ship an Agora-styled theme of OOjs UI.

Compatibility
Browser compatibility is being increased, but currently only modern browsers are targeted. Generally, it is superset of VisualEditor's restricted browser compatibility, which is especially restricted due to VisualEditor's use of contentEditable.

Creating a button
Buttons, like all other user controls, are a kind of widget. Widgets combine a view, in the form of DOM nodes, and a controller in the form of DOM event handling. To create a button, use the  operator and the   class. The first and only argument to a button's constructor is its configuration. The button is now created, and has a label which reads "Click me!", but is not yet visible to the user, as it must first be attached to the DOM. It can be attached by passing btn.$element to a jQuery DOM manipulation method.

Buttons, like all widgets, mixin EventEmitter. To respond to the button being clicked, attach an event handler to the button's  event. Once the button is attached, the label can be changed using the  method.

Elements and mixin classes
Nearly everything in OOjs UI is an element with a  – it's a piece of HTML.

But there are classes that you can mix in to other classes. These don't represent a piece of HTML but change the behavior of a class that does. E.g. OO.ui.ClippableElement will watch to see if something extends beyond screen boundary and if so will add a scrollbar. E.g. a menu toolgroup or list toolgroup will automatically gain a scrollbar if the window isn't tall enough to fit it, and will adjust as you scroll the window. (Google Docs, Mingle, etc. don't do this! You have to)

LabeledElement is another mixin. You pass it a config parameter of what the label text is and then it handles associating a label with an item. It provides a  representing the label, and then when you compose the item in a group such as a menu, the container lays out the item and its label.

Complex: displaying a modal dialog
Let's consider creating a dialog. The Flow extension displays comments on a topic, and there are buttons you can click to [Hide] and possibly [Delete] and [Suppress] a comment. When you click one of these buttons, a modal dialog appears with a text field in which you enter a reason why you Hid/Delete/Suppressed the comment.

Building the dialog
You create your own subclass of OO.ui.Dialog (see [https://doc.wikimedia.org/VisualEditor/master/#!/api/OO.ui.Dialog online documentation), say, and then let a dialog manager create instances of it as needed.

(It's similar to ve.ui.MWBetaWelcomeDialog.)

You "create" the class simply by defining your constructor object, by convention it starts with a capital letter.

config
(jsduck convention @cfg for the configuration object)

We saw in the simple example of creating a button that clients pass in a config object. This is a simple object containing static config values.

All constructors in OOjs UI take a config object as their first and only optional parameter; any required parameters come before it. When implementing a class you can override the config object you're passed using  before calling the parent constructor, in order to get parents to do what you want.

Our "reason" dialog could extend the config object with, and tell its parent it wants to be a small dialog with.

OO.ui.Element, the base class for most elements, has its own config parameters. These include, which lets you bind the element to a different jQuery context than the one in which you create it, see

Element
We saw in the simple example of creating a button that after creating an element it has a  property, which is the jQuery object representing its DOM fragment.

So after calling parent in your constructor, the object has this  property (by default a  ), and it's an unattached DOM element.

When someone uses your class
When some creates an instance of the class, the class's initialize gets called, and e.g. in initialize
 * compose the dialog from other things like layout, a [Continue] button, etc.

All dialogs have a head - body - foot.Foot is where you put buttons. These are jQuery things, so. It's a jQuery thing, not an OO.ui element.


 * Why a separate initialize?
 * With the exception of a Window, you can do anything you want in a constructor. initialize is separate because you have to wait for the iFrame to instantiate. More on lifecycle of objects later.

Want the text of the welcome message to be able to scroll, so it doesn't just $body.html( 'Some stuff here'), it creates a OO.ui.PanelLayout eventually putting that in the body with this.$body.append.

The [Save] button in the dialog
Add a [Save] Button. This is the same as the simple example, just  with the first and only constructor param being the config, where you say In order for the continueButton to to do something, you must handle click events, so using the oojs framework, call. Normally you'd just have a single string in here giving your 'onContinueClick' function name, not an array, and lower-down you would write. Because on close you specify an action signifying (to you, the writer of the dialog) whether to accept what happened, so you can decide whether to abort or toss.

Because you have to pass a parameter, can't just specify 'close'; the array is a shortcut for.

At bottom of class, stick the class in the dialog factory (see later).

Window lifecycle
Window has
 * construct (doesn't do much since have to wait for initialize )
 * initialize
 * each time the thing is used (i.e. before, can happen multiple times)
 * then visible
 * then invisible
 * then invisible
 * then invisible
 * then invisible

They're pre-bound by parent class.

Actual object instantiation
When does this dialog appear? Most likely in response to some handler, e.g. the [Delete] button's click handler,

To make the dialog modal, you put it in an overlay div, and such. OOjs UI is a robust system, so it solves this properly, at the cost of overhead.

windowSet
windowSet is a modal dialog manager, ensures only one is active at any time, handles the overlay.

You would have a global-ish mw.flow.windowSet created in UI setup and attach this to the DOM somewhere.

Assuming we have more than one kind of dialog, the same early-on initialization would instantiate a dialog factory.

mw.flow.dialogs = new OO.ui.DialogFactory;

no parameters.

This allows the dialog Class code at end to  mw.flow.dialogs.register( mw.flow.ReasonDialog );

is the thing that adds to the page.

back to the initiating button
The click handler for the button that pops up this dialog is just mw.flow.windowSet.open( 'reason', { 'type': 'hide', 'author': 'Roan' } )

where the last param is the config for the dialog. The window set will create the dialog that the factory maps to 'reason', and pass it a config object.

The way we to change the label for different reasons is in  swap out the label with setLabel

The dialog
The dialog has
 * a [Save] button
 * a [Save] button
 * a [Save] button

Responding to the Save button
To do some work, the idiom is 'close, 'apply'.

In dialog's onClose if action is 'apply', actually do something, then call parent.close.

E.g. MWReferenceDialog.js onDialog, Calling parent, actually have to know parent class to call it. OO.ui.Dialog.prototype.onClose.call( this, action ); N.B. could do OO.ui.Dialog.prototype.onClose.apply( this, arguments ); but often subclasses have different arguments.

And then look at 'action', and do the right thing.

mw.beta.WelcomeDialog had continue button panelLayout.$.append( object );

E.g. in onOpen, could focus the text input.

Smarter dialog handling
If button is conditional, see ReferenceDialog, basic concept is on 'change' event, check if  then

'change' is smart, doesn't waste time until actual change.

this.$
jQuery's  is bound to the current document. With OOjs UI your components are often in an iframe, so the usual  will not find them – they are in a different document. So  provides   which is bound to the appropriate document.

Widgets
...
 * ButtonWidget - a button that supports 1 line of text
 * IconButtonWidget - a button with a label and an icon
 * PopupWidget - a flyout dialog box that is connected to a particular DOM element
 * PopupButtonWidget - an icon that when clicked opens up a flyout dialog.