Web Components behaviours

A Web Components widget library

Feature gives generic behaviours to Web Components.
Actually I’ve defined the following Features, more will be added

WEBF_CONTAINER

var WEBF_CONTAINER=webf_features({
    methods: {
        __addToContainer: function(){
            var self=this;
            this._components.forEach(function(e){
                self.appendChild(e);
            });

        }
    },
    apply_after: function(){
        if (!this._components) this._components=[];

        this._addToContainer();
    }
},WEBF_I18N,WEBF_FEATURE("WEBF_CONTAINER"));

The Web Components may have sub components. By default they are added to the component itself, more generally the _addToContainer will manage them.

WEBF_CORE

var WEBF_CORE=webf_features({
    apply_before: function(){
        var self=this;
        var webfC=this._hasAscendantFeature("WEBF_CONTAINER");
        if (webfC){
            if (!webfC._components) webfC._components=[];
            self.__parent=webfC;
            webfC._components.push(self)

        }
    },
    apply_after: function(){
        var webfC=this._hasAscendantFeature("WEBF_CONTAINER");
        var self=this;
        if (webfC){
            var self=this;
            if (!webfC._components) webfC._components=[];
            self.__parent=webfC;
        }
    }
},WEBF_FEATURE("WEBF_CORE"),WEBF_CONTAINER);

If a component is inside a container (_components), it will be stored and set the _parent component

New Feature:

Now it manages also Html content. Every html children of a web component are stored. After the component is built, the query $(“[content]”) search for nodes reclaiming html content and append content to them. Es.

           xtag.register('g-accordion-item-1', webf_features({
            methods: {
                TAG:function(data, t){
                    var self=this;
                    var t=t ? t : this._build();
                    t
                            .tag('DIV')
                            .tag("P").att('content',"")
                            .etag('DIV')
                    return t;
                },
            }
        }, WEBF_ACCORDION_ITEM));
    })();

    <g-accordion-item-1><h1>Title</h1></g-accordion-item-1>
 
generates:

    <g-accordion-item-1><div><p><h1>Title</h1></p></div></g-accordion-item-1>
 

TODO:

– I should find a way to pass the model when a component is created via document.createElement(“”).
– A way to postpone component implementation. Es. a dialog could be created when a _show_popup is called and destoryed on _hide_popup
– _webfImpl customization. the “style” attribute of a web component could be passed to _webfImpl.

WEBF_TRAVERSABLE

var WEBF_TRAVERSABLE=webf_features({
    name: "WEBF_TRAVERSABLE",
    apply_before: function(){},
    apply_build: function(){},
    apply_after: function(){
        if (this.children.length)
            this._webfImpl.setAttribute("tabindex",
              webf_indexOf(this.parentNode.children,this));
    }
},WEBF_FEATURE("WEBF_TRAVERSABLE"));

Set the tabindex attribute. May be it will manage also the Focus

TODO:

– should I manage focus here ?

WEBF_CLICKABLE

not yet implemented

 

TODO:

-Is it necessary ?

WEBF_ARMED

var WEBF_ARMED=webf_features({
    accessors: {
        armed : {}
    },
    methods : {
        _armed: function(v){}
    },
    apply_after: function(){
        var self=this;
        xtag.addEvent(this.querySelector(".menu_item"),"mouseenter",function(){
            // hide all descendant popup
            var popup=self._getAscendantFeature("WEBF_POPUP")[0];
            if (popup){
                popup._getDescendantFeature("WEBF_POPUP").each(function(ix,e){
                    e._hide_popup(self);
                });
                popup._getDescendantFeature("WEBF_MENUITEM").each(function(ix,e){
                    e._armed(0);
                });
            }
            self._armed(1);

            var popup=self._getDescendantFeature("WEBF_POPUP");
            if (popup.length>0){
                popup[0]._show_popup(self);
            }
        });
        xtag.addEvent(this.querySelector(".menu_item"),"mouseleave",function(){
        });
    }
},WEBF_FEATURE("WEBF_ARMED"));

The component is armed, the method _armed is called on the mouseenter event (it must be implemented by the web components).
If the component is inside a menu, hide all the submenus
If the component has a menu show it.

WEBF_GROUP

to be implemented

Now the feature manages groups of selectable components using WEBF_SELECTABLE and WEBF_VALUE. Every time a WEBF_SELECTABLE component is Selected, all other selectable components are deselected. The value of this component is the value of the selected component.

It can be used with Radios

WEBF_BUTTON_GROUP

to be implemented

WEBF_STATUS

 var WEBF_STATUS=webf_features({
    accessors : {
        status: {
            get: function(){ return this.dataset.status; },
            set: function(v){
                this.dataset.status=v;
                if(this._status)
                    this._status(this.status);
            }
        }
    },
    methods: {
        toggle: function(){ this.status=!this.status; },
        _action: function(){
                this.status=this.status!="true";
        },
        _status: function(){  } // implements status change

    },
    apply_after: function(){
        this.addClass(this._webfImpl,"status");
    }
},WEBF_FEATURE("WEBF_STATUS"));

This component has a status. It should implement the _status method.update the UI.

New Feature:

Now support WEBF_GROUP. if the component is inside a WEBF_GROUP the click (action) will select the component in the group . Es.

TODO:

It should manage also multistatus.

WEBF_ACTION

var WEBF_ACTION=webf_features({
    name: "WEBF_ACTION",
    accessors : {
        action: {
            get: function(){return this.__action; },
            set: function(v){ this.__action=v; }
        }
    },
    methods: {
        _action: function(e){
            if (this.__action){
                this.__action.apply(this,[e]);
            }
            e.stopPropagation();

        }
    },
    apply_after: function(){
        this.addClass(this._webfImpl,"action");
        $(this).click(function(e){
            var popupF=this._hasAscendantFeature("WEBF_POPUP");
            if (popupF){
                if (!this._hasChildrenFeature("WEBF_MENU"))
                    this._getAscendantFeature("WEBF_MENU").forEach(function(e){
                        e._hide_popup();
                    });
            }
            if(this._action) this._action(e);
            e.stopPropagation();
        })
    }
},WEBF_FEATURE("WEBF_ACTION"));

If the component is inside a Menu, close the menu unless it has an inside menu, then execute the component action. Use the web component action property to assign it.

New Feature:

Now support WEBF_SELECTION. if the component is a WEBF_ITEM, the action will select the the component . Es.

WEBF_INPUT

var WEBF_INPUT=webf_features({
    name: "WEBF_INPUT",
    accessors : {
        action: {
            get: function(){ return this.__action; },
            set: function(v){ this.__action=v; }
        }
    },
    methods: {
        _action: function(e){
            if (this.__action){
                this.__action(this,e);
            }
            e.stopPropagation();
        }
    },
    apply_after: function(){
        this.addClass(this.children[0],"action");
    }
},WEBF_FEATURE("WEBF_INPUT"),WEBF_CORE);

Actually I define an action, but I should manage keys

TODO:

-It should manages keys event and give a feedback to the components. Also the action should be when the “enter” key
-Is this the place for input checks? and input formats?

WEBF_BUTTON

var WEBF_BUTTON=webf_features({
    name: "WEBF_BUTTON",
    apply_after: function(){
        this.addClass(this._webfImpl,"button");
    }
    },WEBF_ACTION,WEBF_CORE,WEBF_FEATURE("WEBF_BUTTON"));

The component is a button.

TODO:

Is it necessary?.

WEBF_MODAL

var WEBF_MODAL=webf_features({
    name: "WEBF_MODAL",
    accessors : {
        isModal: {
            get: function(){ return this.getAttribute("modal")==='true'; },
            set: function(v){ this.setAttribute("modal", v ? "true" : 'false'); }
        }
    },
    methods: {
        _show_popup: function(e){
            if (this.isModal){
                var modalPanel = $("g-modal");
                if (modalPanel.length==0){
                    document.body.appendChild(document.createElement("g-modal"));
                }
                // setup size
            $("g-modal")[0]._webfImpl$
                 .css("z-index","5")
                 .css("display","block")
                 .css("visibility","visible");
            }
        },
        _hide_popup: function(e){
            if (this.isModal){
                var modalPanel = $("g-modal");
                if (modalPanel.length>0){
                    $("g-modal")[0]._webfImpl$
                        .css("display","none")
                        .css("visibility","hidden");
                }
            }
        },
    },
    }
},WEBF_FEATURE("WEBF_MODAL"));

A Modal popup has a white transparent background blocking all events. It chains the _hide_popup and _show_popup to hide and show the background.

WEBF_MENU_CONSTRAINTS

not yet implemented

It should manage menu appearence like viewport overflows.

TODO:

It should manage cases like menus going over the viewport. I should use the _apply_position WEBF_POPUP method to check for overflows and correct them.

WEBF_POPUP

var WEBF_POPUP=webf_features({
    name: "WEBF_POPUP",
    methods: {
        _hide_popup: function(e){
            var popup=this._webfImpl$;
            this._getDescendantFeature("WEBF_POPUP").each(function(ix,e){
                e._hide_popup(self);
            });
            popup.css("display","none").css("opacity","0").css("visibility","hidden");
        },
        __adjust_position_popup: function(e, position){},
        _position_popup:function(e){
            var anchor=this.getAttribute('anchor');
            // decide where popup is to be set
            // t=(parent|document|window|parent)
            // top=top|bottom|middle|amiddle
            // bottom=top|bottom|middle|amiddle
            // left=left|right|center|acenter
            // right=left|right|center|acenter
            // ex
            //      t=document top=center left=center <- this is centered
            //      t=document top=middle left=middle <- starts from the middle of the screen
..
            this._apply_position(popup,res);

        },
        _apply_position: function(popup,res){
            for (var key in res) {
              popup.css(key,res[key]);
            }
        },
        _show_popup: function(e){
            var popup=this._webfImpl$;

            this._getDescendantFeature("WEBF_POPUP").each(function(ix,e){
                e._hide_popup(self);
            });
            this._getDescendantFeature("WEBF_MENUITEM").each(function(ix,e){
                e._armed(0);
            });

            var ascPopup = this._hasAscendantFeature("WEBF_POPUP");
            if (ascPopup && ascPopup._webfImpl$.css("z-index"))
                popup.css("z-index",parseInt(ascPopup._webfImpl$.css("z-index")+1));
            popup
                .css("display","block")
                .css("opacity","1").css("visibility","visible");
            this._position_popup(e);
        },

        _toggle_popup: function(e){
            var popup=this._webfImpl$;

            if (popup.css("display")=='none'){
                this._show_popup(e);
            } else
                this._hide_popup(e);
        },
    },

    apply_after: function(){
        var self=this;
        this._webfImpl$.css("z-index",8);
        document.body.appendChild(this._webfImpl);

        if (this._hasAscendantFeature("WEBF_POPUP")){
            this.setAttribute("anchor","top=top left=right");
        }

        var inputF = this.__parent && this.__parent._hasFeature("WEBF_INPUT") ? 
             this.__parent:null;
        if (inputF){
            this.setAttribute("anchor","top=bottom left=left width=width");
            $(inputF).click(function(e){
                self._toggle_popup(inputF);
                e.stopPropagation();
            })
        }
    }
},WEBF_FEATURE("WEBF_POPUP"),WEBF_MODAL,WEBF_CORE);

This components has _hide_popup, _show_popup and _toggle_popup methods. When a popup is showed, the _position_popup method decide where to show in the screen using the attribute anchor, asking to the component for some adjustment via the _adjust_position_popup method. At the end the method _apply_position assigns style to the web component (override this method to check position on screen).

Generally popup is considered to be absolute positioned. So It place the popup directly inside the html body.
If the popup is inside a

  • popup the anchor is top=top left=right
  • input the anchor top=bottom left=left width=width and a click on the input will show the popup

WEBF_MENU

var WEBF_MENU=webf_features({
    apply_after: function(){
        this._webfImpl$.css("z-index",10);
        var actionF=this._hasAscendantFeature("WEBF_ACTION");
        var self=this;
        if (actionF){
            var self=this;
            actionF.__action=function(e){
                self._toggle_popup(actionF);
                e.stopPropagation();
            }
        }
    }
},WEBF_FEATURE("WEBF_MENU"),WEBF_MODAL,WEBF_POPUP);

If a menu is inside an action manage to toggle the popup

New Feature:

Now the component is also a List. The current selected item, is the value of the component Es.

WEBF_MENUITEM

var WEBF_MENUITEM=webf_features({
    apply_after: function(){
        var self=this;
        if (this.children.length)
            this.addClass(this._webfImpl,"menu_item");

        var popupF=this._hasAscendantFeature("WEBF_POPUP");
        if (!popupF){
            this.addClass(this.parentNode,"webf_bbed4b97553b66c304c981e543af5ef3");
        }
    }
},WEBF_FEATURE("WEBF_MENUITEM"),WEBF_ARMED,WEBF_CORE);

This is a menu item.

TODO:

It should go inside WEBF_ITEM.

New Feature:

Now it is also a WEBF_Value, WEBF_Item and WEBF_Selectable

WEBF_FORM

to be implemented

TODO:

WEBF_FORM could be use to fill parameters values by web components.
It should have a submit method which could be a regular submit or an ajax call.

Web components could fill a form model object, used to send data to the server.
Model values could also be checked.

WEBF_TOOLTIP

var WEBF_TOOLTIP=webf_features({
    apply_after: function(){
        var webfF=this._hasAscendantFeature("WEBF_CORE");
        var self=this;
        if (webfF){
            var self=this;

            $(webfF).mouseenter(function(){
                self._show_popup(webfF);
            });
            $(webfF).mouseleave(function(){
                self._hide_popup(webfF);
            });
        }
    }
},WEBF_FEATURE("WEBF_TOOLTIP"),WEBF_CORE,WEBF_POPUP);

If it is inside a component manage showing/hiding on mouseenter/mouseleave

TODO:

Could be applied to all components if the “tooltip” attribute is set.

WEBF_WINDOW

var WEBF_WINDOW=webf_features({
    apply_after: function(){
        var self=this;
        this._webfImpl$.css("z-index",9);
        var actionF=this._hasAscendantFeature("WEBF_ACTION");
        var self=this;
        if (actionF){
            var self=this;
            actionF.__action=function(e){
                self._show_popup(actionF);
                e.stopPropagation();
            }
        }
        $(".WEBF_CLOSE_ACTION").click(function(e){
            self._hide_popup(this);
            e.stopPropagation();
        });
    }

},WEBF_FEATURE("WEBF_WINDOW"),WEBF_POPUP);

Assign z-index for layering.If it is inside an action manage open on click
Manage closing via class “WEBF_CLOSE_ACTION”

TODO:

A feedback should exists to notify show/hide.

WEBF_DIALOG

var WEBF_DIALOG=webf_features({
    apply_after: function(){
        if (!this.getAttribute("anchor")) 
               this.setAttribute("anchor","t=window top=middle left=center");
    }
},WEBF_FEATURE("WEBF_DIALOG"),WEBF_WINDOW);

Assign the anchor to show it in the middle of the screen

TODO:

A feedback should exists to notify ok, cancel. Allow the creation of Dialogs with standard buttons (Like Ok, cancel, yes, no)

WEBF_ACCORDION

var WEBF_ACCORDION=webf_features({
},WEBF_FEATURE("WEBF_ACCORDION"),WEBF_CORE);

 

TODO:

I think this have to be a list, with single or multple selection

WEBF_ACCORDION_ITEM

var WEBF_ACCORDION_ITEM=webf_features({
},WEBF_FEATURE("WEBF_ACCORDION_ITEM"),WEBF_CORE);

 

WEBF_I18N

var WEBF_I18N=webf_features({
    methods: {
        __getKeys: function(lang){ return document._i18nMngr._getKeys(lang); },
        __addKeys: function(keys, lang){ document._i18nMngr._addKeys(keys, lang); },
        __addKey: function(key, value, lang){ document._i18nMngr._addKey(key, value, lang); },

        __i18n: function(){
            var keys = this._getKeys(this.getAttribute("lang"));
            var args=xtag.toArray(arguments);
            for (var i = 0; i < args.length; i++) {
              if (args[i])
                  if (keys[args[i]]) return keys[args[i]];
            }
            return args[ args.length-1];
        },
        __i18N: function(str,alt){
            var keys = this._getKeys(this.getAttribute("lang"));
            return str ? str : keys[alt];
        }
    },
    apply_before: function(){
        if (!document._i18nMngr)
            document._i18nMngr=document.createElement("webf_i18n_manager");

        var content = this.querySelectorAll("i18n");
        for (var j = 0; j < content.length; j++) {
            var e1 = content[j];
            var id= e1.id;
            var d=JSON.parse(e1.textContent);
            this._addKeys(d,e1.getAttribute("lang"));
        }
    }
},WEBF_FEATURE("WEBF_I18N"));

Manages I18N Strings. Components having this feature have access to methods.

__i18n: lookup a keys in sequence if no key is found return the last string
__i18N: if the first parameter exists use it otherwise lookup the second

the language used is defined by the attribute lang of the web components. Languages strings are defined inside the template as follow:

<i18n type="text/i18n" lang="it">
    {
    "Search": "Cerca",
    "searchIcon": "//ssl.gstatic.com/ui/v1/button/search-white.png",
    "Go Back":"Indietro",
    "Previous":"Precedente",
    "Expand":"Espandi",
    "Minimize":"Minimizza",
    "Close Dialog":"Chiudi Finestra"
    }
</i18n>

TODO:

Add methods for number, dates, currency, etc formats and parsing.
Is it usufull to allow runtime language switch ?

WEBF_SELECTABLE

var WEBF_SELECTABLE=webf_features({
},WEBF_FEATURE("WEBF_SELECTABLE"),...);

 
The component is selectable, this features add a selected property to the components. the components should implement the _selected(value) method. The component is selected if value==this

WEBF_VALUE

var WEBF_VALUE=webf_features({
},WEBF_FEATURE("WEBF_VALUE"),...);

 
The component has a value. this features add a value property to the components. the components should implement the _value(value) method.

TODO:

Is this the place to put input checks?

WEBF_SELECTION

var WEBF_SELECTION=webf_features({
},WEBF_FEATURE("WEBF_SELECTION"),...);

 
Manage Selection for a component. It add methods to add and remove selections. WEBF_ITEMs inside the components can be selected. Selection may be “single” or “multiple” based on the attribute “selection”.

Propeties
selected_items: the list of selected WEBF_ITEMs
selected_item: the selected WEBF_ITEM
selection_type: 0 is single selection, 1 multiple selection use "single" or "multiple" to set it

Methods

_selection_update: function(items) // notify that this items are selected
// call _selection_update at the end
_selection_clear: function(item) // clear selections
_selection_add_range: function(start,end) // add a selection range
_selection_remove_range: function(start,end) // remove a selection range
_selection_add: function(item) // select an item/s
_selection_remove: function(item)  // deselect an item/s
// dont call _selection_update at the end
_selection_add_item: function(item)  // select an item
_selection_remove_item: function(item) // deselect an item

TODO:

I should manage CTRL-SHIFT to manage multiple selections

WEBF_LIST

var WEBF_LIST=webf_features({
},WEBF_FEATURE("WEBF_LIST"),...);

 
The component is a list. it is also a WEBF_VALUE and WEBF_SELECTION. the property items return all its WEBF_ITEM children.

TODO:

I should manage adding/removing at runtime

WEBF_ITEM

var WEBF_ITEM=webf_features({
},WEBF_FEATURE("WEBF_ITEM"),...);

 
The component is an item. it is also a WEBF_VALUE and WEBF_SELECTABLE.

TODO:

It should also define a label/title to be used by the container to show it when it is selected.