blackberry / bbUI.js

BlackBerry UI look and feel JavaScript toolkit for WebWorks
Apache License 2.0
312 stars 192 forks source link

bbUI Declarative Markup Change #933

Closed tneil closed 10 years ago

tneil commented 11 years ago

Over the last while we've learned a lot with bbUI. What worked, what didn't work so well and where it excels and also falls short. We've also been asked "When will it be v1.0?". In order to get to v1.0 we need to do a few things.

NOTE: This official v1.0 will only support BB10 styling of controls. Non BlackBerry 10 styling will remain as they are today.

Maintaining State

One of the tricky issues with bbUI is how you maintain state and data within your application. Since everything is DOM based, and each screen is added/removed from the DOM as needed, you essentially need to create a model in the background to be able to remember the state of your current screen.

By moving the markup for bbUI from DOM based markup to JavaScript based markup, it provides a model where the state of each screen is maintained in the model and the screen can be rendered as needed.

Optimizing Memory Usage

One of the great things with bbUI is the fact that it optimizes the amount of memory being used by the application. This is typically done by minimizing the amount of DOM that is being parsed/rendered by the browser. It uses onscreenready and ondomready events so that styling of the DOM elements is done when they are disconnected from the live DOM to reduce layout computations and allows all manipulation to happen in memory before they get rendered by the browser.

Moving to a JavaScript markup language allows for this same type of control of manipulation and timing for when to apply changes to the DOM. Essentially you can think of the JavaScript markup/model as a "shadow DOM" of the actual content that is being rendered. Similar to a scene graph in OpenGL programming. Your model is always separated from the rendering and updates to the model trigger updates to the rendering but in a very controlled fashion.

This behind the scenes model allows for even more granular control around what to show when and how to optimize the DOM in the browser.

Screen Stacking and Tabs

The current model for push/pop of screens in bbUI is simple and gets the job done. However its simplicity can also be a drag. This is especially true for dealing with tabbed interfaces as well as the concept of a "sheet" as known in Cascades. Tabbed interfaces are currently very manual and cumbersome and sheets are a concept of layering a screen over-top of your existing screen

The markup based solution will take on the Cascades concepts of screen stacking, tabbing and sheets to create a much more flexible model for application navigation

Ease of Use

One thing we don't want to lose is the ease of use and declarative nature of bbUI. Our goals of the new markup language will be to take on the same ease of use patterns, but better align them with the existing Cascades structure and naming. This will also better align the BlackBerry 10 UI guideline terminology and cause less confusion for web developers when reading other BlackBerry 10 material.

Custom Content

One thing that all web developers want is the ability to add their own HTML/JS/CSS content to a bbUI styled application. We will ensure that this same capability holds true for the new markup language. There will be the ability to create containers that can be placed in a layout where they can add their own custom HTML/JS/CSS to add to the bbUI application experience.

Layouts

One place that bbUI falls short is providing some nice simple to use layouts for content. These are things that are provided in Cascades today that are not made available in bbUI (DockLayout, StackLayout, FlowLayout). These will be added as concepts in the v1.0 of the toolkit

UI Components

As issues get created for each of the UI components we'll add links to them from the below list:

Application Structure:

Controls:

Layouts:

Lists:

Text:

Initializing the toolkit and first screen of the application


var navigationPane = {
  component: bbUI.NavigationPane,
  id: 'myNavigationPane',
  page: {
    component: bbUI.Page,
    id: 'page1',
    content: [
       {
           component: bbUI.Label,
           id: 'myLabel',
           text: 'This is a Label'
       }
    ]
  }
};

// Initialize the toolkit and show our pane
bb.init({
  coloredTitleBar: true,    
  controlsDark: true,
  listsDark: true,                 
  highlightColor: '#00A8DF',  
  pane: initialPane
});

Example of markup for a stacked navigation pane structure:

var navigationPane = { // bbUI.NavigationPane
    component: bbUI.NavigationPane,
    id: 'myNavigationPane',
    backButtonsVisible: false,

    page: { // bbUI.Page
        component: bbUI.Page,
        id: 'screen1',
        content: [
            { // bbUI.Container
                component: bbUI.Container,
                content: [
                    { // bbUI.Button
                        component: bbUI.Button,
                        id: 'button1',
                        caption: 'Click Me',
                        onclick : function (){
                                alert('you clicked button 1');
                            }           
                    }, // end of bbUI.Button
                    {  // bbUI.Button
                        component: bbUI.Button,
                        id: 'button2',
                        caption: 'Click Me Too',
                        onclick : function () {
                                alert('you clicked button 2');
                            }                           
                    } // end of bbUI.Button
                ]
            }, // end of bbui.Container
            { // bbUI.Toggle
                component: bbUI.Toggle,
                id: 'myToggle',
                yes: 'Yes',
                no: 'No',
                onchange: function(){
                        alert('you changed me');
                    }
            } // end of bbUI.Toggle
        ],
        actions: [
            { // bbUI.ActionItem
                component: bbUI.ActionItem,
                title: 'Push',
                placement: ActionBarPlacement.OnBar,
                ontriggered: function()
                    var pane = bbui.$('myNavigationPane');
                    pane.push(pane.$('screen2'));
                }   
            }, // end of bbUI.ActionItem
            { // bbUI.ActionItem
                component: bbUI.ActionItem,
                title: 'My overflow Item',
                placement: ActionBarPlacement.InOverflow,
                ontriggered: function() {
                    var pane = bbui.$('myNavigationPane');
                    pane.push(pane.$('screen3'));
                }
            } // end of bbUI.ActionItem
        ]
    },

    attachedObjects: [
        { // bbUI.Page
            component: bbUI.Page,
            id: 'screen2',
            content: [
                { // bbUI.Button
                    component: bbUI.Button, 
                    id: 'button1',
                    caption: 'Click Me',
                    onclick : function () {
                            alert('you clicked button 1');
                        }                   
                } // end of bbUI.Button
            ]
        }, // end of bbUI.Page
        { // bbUI.Page
            component: bbUI.Page,
            id: 'screen3',
            content: [
                { // bbUI.Toggle
                    component: bbUI.Toggle,
                    id: 'myToggle',
                    yes: 'Yes',
                    no: 'No',
                    onchange: function()  {
                            alert('you changed me');
                        }
                } // end of bbUI.Toggle
            ]       
        } // end of bbUI.Page
    ]
}; // end of bbUI.NavigationPane

Example of a tabbed pane for the base of the application:

myTabbedPane = { // bbUI.TabbedPane
    component: bbUI.TabbedPane,
    id: 'myTabbedPane',
    showTabsOnActionBar: true,

    onactivetabchanged: function(activeTab) {
                            console.log(activeTab.id);
                        },

    activeTab = { // bbUI.Tab
        component: bbUI.Tab,
        id: 'tab1',
        title: 'Tab 1',
        content: { // bbUI.Page
            component: bbUI.Page,
            id: 'page1',
            content: [
                { // bbUI.Button
                    component: bbUI.Button,
                    id: 'button1',
                    caption: 'Click Me',
                    onclick : function (
                            alert('you clicked button 1');
                        )                   
                } // end of bbUI.Button
            ]
        }, // end of bbUI.Page
        ontriggered: function() {
                alert('you pressed tab 1');
            }
    }, // end of bbUI.Tab

    tabs: [
        { // bbUI.Tab
            component: bbUI.Tab,
            id: 'tab2',
            title: 'Tab 2',
            placement: ActionBarPlacement.InOverflow,
            content: { // bbUI.Page
                component: bbUI.Page,
                id: 'page2',
                content: [
                    { // bbUI.Label
                        component: bbUI.Label,
                        text: 'This is tab 2'
                    } // end of bbUI.Label
                ]           
            } // end of bbUI.Page
        }, // end of bbUI.Tab

        { // bbUI.Tab
            component: bbUI.Tab,
            id: 'tab3',
            title: 'Tab 3',
            placement: ActionBarPlacement.InOverflow,
            content: { // bbUI.Page
                component: bbUI.Page,
                id: 'page3',
                content: [
                    { // bbUI.Label
                        component: bbUI.Label,
                        text: 'This is tab 3'
                    } // end of bbUI.Label
                ]           
            } // end of bbUI.Page
        } // end of bbUI.Tab
    ]
}; // end of bbUI.TabbedPane
glasspear commented 11 years ago

:+1: I like where this is heading. I'd love to see a more detailed sample to make sure I am wrapping my head around this properly. Or will it be similar to using QML in many ways?

I am curious how some of this will port back to PlayBook and legacy BBOS, or will they be frozen in their current state without new updates?

Will the custom content pieces allow for custom controls to be plugged in? i.e. if someone wrote a searchView control it could hook into bbUI like any built in control and be called in the page declaration?

Let me know where I can help. Will current functionality just be melded to work in the new model or will a lot of it need to be re-written from the ground up?

tneil commented 11 years ago

It will be very similar to using QML in many ways... We will be creating issues for each individual control complete with the details of the markup and how they will work. I'll make the list in this issue link to those issues once we have them in place.

The official v1.0 with this markup will only support the BB10 styling (including PlayBook BB10 styling) but not traditional BBOS or PlayBookOS styling.

Yes we will have custom control capabilities along with the ability to add a container with adhoc HTML.

We will be pretty much re-using the existing bbUI DOM and CSS styling merged with Anzor's PageStack framework. Basically mashing the two together with JavaScript markup/properties/events that look as close to the Cascades QML markup and page/screen handling as possible.

emmanix2002 commented 11 years ago

This is looking great. Keep up the good work @tneil

splatterb0y commented 11 years ago

Ohhhh that sounds amazing! QML like style for WebWorks Elements would be awesome. Maybe base WebWorks on a JS-Library like jQuery or Zepto.js to make it smaller and use their "more advanced functions" and provide WebWorks with "jQuery Styled" hide and show events and functions.

Something like $("#tab").hide();

instead of the typical document.getElementById() stuff.

tneil commented 11 years ago

Yup, we will have a quick and easy selector to grab a control based on its id within its scope :+1:

Something like:

 bbUI.$('tab1').hide();
tneil commented 11 years ago

I just added the NavigationPane structure to give you an example of what it will look like

paroxysm commented 11 years ago

Well, there goes MVVM support, I refuse to part with angularjs.

splatterb0y commented 11 years ago

I know this sounds completely crazy and shit, but if we make a declarative bbUI.js why not make a plugin that translates declarative WebWorks Elements to QML Elements and just fills the pages with the loaded "web views" for the application? I hope you understand what I try to point out.

emmanix2002 commented 11 years ago

@splatterb0y I can't say with absolute certainty but I believe that might result in some unnecessary overhead. Considering the memory footprint the plugin would leave, I believe that would be totally against the low memory footprint of the current versions of bbUi.js Just my thoughts though.

splatterb0y commented 11 years ago

I guess you could "precompile" the WebWorks Markup to native QML and just inherit the logic and styling of all the other components. It would be a nice Idea if you ask me. QML for me has to bad debugging functions (no console.log and stuff without terminal and shit, webworks developer mode is pretty awesome in this point) and the apis are to limited why is there no Navigator.geoLocation and a fancy api for AJAX Request in QML so all WebWorks Devs could move to QML without having to get their hands dirty with C++

kwallis commented 11 years ago

We are looking at this bbui transition as a first step in an evolution to more closely align WebWorks, QML, and the javascript/web platform across WebWorks and native. Lots more to come. But these are definitely interesting thoughts to consider to perhaps support a porting story.

Sent from my BlackBerry 10 smartphone. From: Christian Ziegenrücker Sent: Saturday, July 6, 2013 9:20 AM To: blackberry/bbUI.js Reply To: blackberry/bbUI.js Subject: Re: [bbUI.js] bbUI Declarative Markup Change (#933)

I guess you could "precompile" the WebWorks Markup to native QML and just inherit the logic and styling of all the other components. It would be a nice Idea if you ask me. QML for me has to bad debugging functions (no console.log and stuff without terminal and shit, webworks developer mode is pretty awesome in this point) and the apis are to limited why is there no Navigator.geoLocation and a fancy api for AJAX Request in QML so all WebWorks Devs could move to QML without having to get their hands dirty with C++

— Reply to this email directly or view it on GitHubhttps://github.com/blackberry/bbUI.js/issues/933#issuecomment-20556712.


This transmission (including any attachments) may contain confidential information, privileged material (including material protected by the solicitor-client or other applicable privileges), or constitute non-public information. Any use of this information by anyone other than the intended recipient is prohibited. If you have received this transmission in error, please immediately reply to the sender and delete this information from your system. Use, dissemination, distribution, or reproduction of this transmission by unintended recipients is not authorized and may be unlawful.

ghost commented 11 years ago

Is there any date on which we can try the first beta of 1.0?

splatterb0y commented 11 years ago

@ibewugb Since 0.9.7 is still in the making afaik, I guess there is no eta. If you ask me it should come as soon as possible but I am not sure if @tneil is a Magician or just a developer. :D

anzorb commented 11 years ago

@paroxysm, BBUI.js 1.X will be built from the ground up to be MVVM itself, while allowing you to create custom components using a MVVM framework of your choice.

Here are some examples: Each control has an 'el' property that contains its rendered DOM (once it has been rendered), so you can access using control.el (or this.el). Changing properties on the control triggers events that you can listen to, and make UI changes accordingly.

Custom controls can be written using any framework and exposed so that these can be declared using the new syntax - this keeps your UI definitions clean, while you can modify your custom control separately, without touching the UI - great practice for MVVM development.

There will also be a HTMLTemplate control, that simply renders a piece of HTML using a templating language, so you may use it as a control. This is very similar to AngularJS' templating, and data can be passed to build dynamic views where necessary as well.

UI needs to be pixel density independent. If you want to use reference CSS resolution, versus the full physical device resolution - components should scale with no work. This will become especially important as the screen densities continue to increase. It needs to just work - no matter what screen factor, or screen density or viewport setting.

Overall, this project will use some of the best cutting edge HTML5/JS tech, making maintenance and updates clean and simple. The navigation bits will not have to be done by hand anymore, allowing the developers to spend less time figuring out how to stack pages, and more time on writing dynamic, reusable high performance content.

splatterb0y commented 11 years ago

@anzorb That sounds amazing. I totally dig this if the performance will be comparable to Cascades. At the moment CSS3 Transitions and stuff are relatively slow compare to Cascades and a "fancy application" with lots of css3 stuff is not very fluid. :(

tneil commented 11 years ago

There are a few items to finish up in v0.9.6 and then we are into the creation of a v1.0.1 branch were we will be doing most of our work.

Still working through some final bits around licensing to ensure we bring in the full BB10 experience that will also likely be tied to a v1.0 release of the framework.

I know @anzorb has a great base to start on and is working hard at getting it ready to be merged with bbUI. Work is underway, just need to clear out a couple of administrative items and then we'll have some code for you to check out :o)

My thoughts are to re-create all the samples with the markup and documentation before merging the code.. That will ensure that we walk through the use case scenarios to mary the functionality together with the markup

gcsantiago commented 11 years ago

I will follow :+1:

paroxysm commented 11 years ago

@anzorb, from your posting my understanding is that ui components will be js "classes" that you can leverage to manipulate the UI. If that's the case, when integrating with angularjs, that means I won't need their directives to perform ui component re-styling in the case that a model changes(i.e. rather than using ng-repeat for an array of objects, I have a reference to a bbui, say imagelist class, that I can manipulate similar as a data structure and have the ui re-paint as elements in this image class object are added/removed/modified) ?

splatterb0y commented 11 years ago

@anzorb @paroxysm I don't know much about MVVM is this what Ember.js does? Maybe we can integrate bbUI.js with Ember.js because I really like that Ember.js uses Mustache.js and jQuery as a basis, because I heavily really on both of these components.

anzorb commented 11 years ago

@paroxysm, exactly, ng-repeats are used to build lists and other components, so if bbUI provides a listView class, you no longer need to do ng-repeats to display the content.

@splatterb0y we don't want to depend on jQuery, but you can certainly use it in your projects. As for moustache, bbUI.js 1.x relies on Underscore.js (Lodash.js) templates for HTML, which are not that different from moustache, but decrease the number of dependencies.

We don't want to rely on other MV* frameworks (Ember.js, Backbone, Angular), bbUI.js is an MV* framework itself, providing a very unique and simple way to define and build App UIs similar to that of Cascades.