backbone-uikit

This is a set of UI components inspired by the iOS UIKit Framework. The UIKit provides crucial infrastructure to construct and manage web apps.

Getting Started

$ npm install backbone-uikit --save

Components

UIView

The UIView class defines a rectangular area on the screen and interfaces for content in that area.

var UIKit = require('backbone-uikit'); var AppView = UIKit.UIView.extend({ el: 'body', render: function() { this.$el.empty(); // your code return this; } }); module.exports = new AppView();

Views can embed other views and create sophisticated visual hierarchies. This creates a parent-child relationship between the view being embedded (known as the subview) and the parent view performing the embedding (known as the superview). Normally, a subview’s visible area is not clipped to the bounds of its superview, but you can use the overflow CSS property to alter that behavior. A parent view may contain any number of subviews but each subview has only one superview.

To add a subview to another view, you apply the method addSubview:

render: function() { this.$el.empty(); // Optional this.addSubview(new UIKit.UIView({ class: 'my-view' })); return this; // Required by Backbone }

To remove a view with all the subviews and its el from the DOM, and calls stopListening to remove any bound events that the view has listenTo'd, you can employ the destroy method:

var anotherView = new UIKit.UIView({ class: 'my-view' }); // ... anotherView.destroy();

Methods to Override

Initialization

UIView.prototype.initialize() do a lot of useful things for you:

initialize: function(options) { UIView.prototype.initialize.apply(this, [options]); // your code }

Rendering

Sometimes you need to determine the actual size of the view. This is possible only after building the DOM. For this purpose it is convenient to use the following approach.

render: function() { this.$el.empty(); setTimeout(() => { this.layout(0); // Call it as you wish }, 0); return this; }, layout: function() { var rect = this.size(); // For example: { width: 100, height: 200 } }

All Methods and Properties

Event Handling

A view can handle touch events by jGestures and other events defined by jQuery.

events: { // Touch Events and Gestures by jGestures touchstart: 'touchstartHandler', touchend: 'touchendHandler', tapone: 'taponeHandler', swipemove: 'swipemoveHandler', pinch: 'pinchHandler', // Mouse Events by jQuery mousedown: 'mousedownHandler', mouseup: 'mouseupHandler', mousemove: 'mousemoveHandler', mouseover: 'mouseoverHandler', mouseout: 'mouseoutHandler', mouseenter: 'mouseenterHandler', mouseleave: 'mouseleaveHandler' }

Default Event Handlers for Touch Events and Gestures

touchstartHandler: function(event) {}, touchendHandler: function(event) {}, taponeHandler: function(event) {}, swipemoveHandler: function(event, obj) {}

Configuring the Event-Related Behavior

userInteractionEnabled: false

Managing the View Hierarchy

superview: null, subviews: [], addSubview: function(view [, '.element' | $element]) {} bringSubviewToFront: function() {}, sendSubviewToBack: function() {}, viewDidAppear: function() {}, viewDidDisappear: function() {}

Availability, Visibility and Selection

// Handle availability // use '.ui-dis' in CSS disabled: false, disable: function() {}, enable: function() {}, // Handler visibility // use '.ui-hid' in CSS hidden: false, hide: function() {}, show: function() {}, // Handle selection // Use '.ui-sel' in CSS selected: false, deselect: function() {}, select: function() {}

Animating Views

To achieve the highest frame rate separate animated inline styles with the static ones from the CSS files.

calculateAnimatedStyles: function(animated) {}, applyAnimatedStyles: function(animated) {}, transitionEndHandler: function(event) { event.stopPropagation(); }

More sophisticated content is presented by subclassing UIView and implementing the necessary drawing and event-handling.

UIButton

this.addSubview(new UIKit.UIButton({ class: 'backButton', label: 'Back', action: () => { this.goBack(); } }));

UILabel

this.addSubview(new UIKit.UILabel({ text: 'Text' }));

UILabel can display some data from a model and watch changes.

var model = new Backbone.Model({ data: 1 }); this.addSubview(new UIKit.UILabel({ model: model, attribute: 'data' }));

UITextField

this.addSubview(new UIKit.UITextField({ class: 'my-text-field-01', name: 'test', placeholder: 'One line ...', changeHandler: function(event) { console.log('event.data.value = ', event.data.value); console.log('this.value = ', this.value); } })); var user = new Backbone.Model({ phoneNumber: '+12345678910' }); this.addSubview(new UIKit.UITextField({ class: 'phone-text-field', name: 'phone', type: 'tel', autocomplete: 'tel', placeholder: 'Your phone', model: user, attribute: 'phoneNumber' }));

UITextView

this.addSubview(new UIKit.UITextView({ class: 'my-text-view', placeholder: 'Let us know more ...', text: '' }));

UISegmentedControl

this.addSubview(new UIKit.UISegmentedControl({ items: [{ label: 'First' }, { label: 'Second' }, { label: 'Third' }], changeHandler: (index) => { console.log(index); } }));

UIStepper

this.addSubview(new UIKit.UIStepper({ class: 'my-stepper', value: 0, minimumValue: 0, maximumValue: 1000, stepValue: 1, autorepeat: false })); var model = new Backbone.Model({ number: 1 }); this.addSubview(new UIKit.UIStepper({ model: dataModel, attribute: 'number', minimumValue: 0, maximumValue: 10, changeHandler: function(value) { console.log('changeHandler, value = ', value); } }));

UIImageView

this.addSubview(new UIKit.UIImageView());

UINavigationBar

var submitBtn = new UIButton({ label: 'Submit', action: function() {} }); this.addSubview(new UINavigationBar({ leftBarItems: [], centerBarItems: [new UILabel({ text: this.title })], rightBarItems: [submitBtn] }));

UITabBar

this.addTabBar(new UIKit.UITabBar({ selectedIndex: 0 }));

UIScrollView

var scrollView = new UIScrollView({ class: 'my-scroll-view', minimumScale: 0.08, maximumScale: 3, testHandler: function() { // Your code } }); var contentView = new ContentView(); this.addSubview(scrollView); scrollView.addSubview(contentView);

UIActivityIndicatorView

this.addSubview(new UIKit.UIActivityIndicatorView());

UIAccordion

this.addSubview(new UIKit.UIAccordion({ class: 'my-accordion', openedIndex: 2, buttons: [ new MyAccordionButtonView({ title: 'First' }), new MyAccordionButtonView({ title: 'Second' }), new MyAccordionButtonView({ title: 'Third' }) ], items: [ new UIView({ class: 'first-acc-view' }), new UIView({ class: 'second-acc-view' }), new UIView({ class: 'third-acc-view' }) ] }));

UISelect

var MySelectItemView = require('./MySelectItemView'); var collection = new Collection(); collection.add([{ title: 'First', description: 'First model' }, { title: 'Second', description: 'Second model' }, { title: 'Third', description: 'Third model' }]); this.addSubview(new UIKit.UISelect({ class: 'my-select', listClass: 'my-select-list', // label: 'Select ...', // appearance: 'up', collection: collection, // disabled: true, ItemView: MySelectItemView, changeHandler: function() { console.log(this.selectedIndex); } }));

UICheckbox

this.addSubview(new UIKit.UICheckbox({ name: 'my-checkbox', checked: false }));

Modality

Alert

UIKit.alert({ title: 'Title', message: 'This is a message.', okButtonLabel: 'OK' });

Confirm

UIKit.confirm({ title: 'Title', message: 'This is a message.', cancelButtonLabel: 'Cancel', okButtonLabel: 'OK' }) .done(function() {}) .fail(function() {});

Prompt

UIKit.prompt({ title: 'Title', message: 'This is a message.', placeholder: 'This is a placeholder', value: '', cancelButtonLabel: 'Cancel', okButtonLabel: 'OK' }) .done(function(value) { console.log('ok, value = ', value); }) .fail(function() {});

ActionSheet

UIKit.actionSheet({ title: 'Title', actions: actions, cancelButtonLabel: 'Cancel' }) .done(function(data) { console.log('ok, index = ', data); }) .fail(function() { console.log('cancel'); });

Modal

UIKit.modal({ contentView: new SomeView() });

How to close the modal view from the content view:

this.superview.hide();

All the modal dialogs trigger event 'uikit-modal' on Backbone with a promised view as a data. You can use it to handle the view in various situations.

// appModel this.listenTo(Backbone, 'uikit-modal', (view) => { this.set({ modal: view }); }); // someRouter if (appModel.get('modal')) { switch (appModel.get('modal').state()) { case 'pending': appModel.get('modal').destroy(); break; case 'resolved': case 'rejected': appModel.set({ modal: null }); break; default: } }

Development

$ npm run dev

Dependencies

When the library is built, it excludes jQuery, Underscore, Backbone and jGestures from the built library. Consumers of the built library will provide all the dependencies for the library. If the consumer uses an AMD loader, then the built file will ask for jquery, underscore, backbone and jgestures as AMD dependencies. If the consumer just uses browser globals and script tags, the library will grab the $, _, Backbone and jGestures global variables and use them for the dependencies.

The built library also does not include require.js in the file, but instead uses almond, a small AMD API implementation, that allows the built file's internal modules to work. These internal modules and this version of almond are not visible outside the built file, just used internally by the built file for code organization and referencing.

File structure

This project creates a library called uikit.js and a file uikit.css in the dist folder.

How to build the library

$ npm run build

This will generate the built files `dist/uikit.js` and `dist/uikit.css`.

Test the built file by running these files:

What to tell developers of your built library

You can tell them this library can be used with other AMD modules, or it can be used in a project that uses browser globals and HTML script tags.

License

Copyright © 2017 Andrey Aleshkov

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.