Extjs 5 htmlEditor resize bug

I just finished including some html editors in windows that contained forms which were maximizable where the intent is to have the htmleditor field grow on resize and fill the blank space. I used the following code placed on the window to resize the editor:

listeners   : {
    resize : function(windowItself, width, height){
        // 250 is the height of all other form elements combined.
        this.down('htmleditor').setHeight(height - 250);
    }
}

However, although the box itself appears to have resized properly after running the code when doing some testing the actual textarea is placed in an iframe and this iframe did not resize with the outer div. The following CSS will correct this error in Extjs 5.x

.x-htmleditor-iframe{
    height: 100% !important;
}

Adding Midas commands (like strikethrough) to the Extjs 5.x HtmlEditor

Almost ten years ago Mozilla introduced a series of commands that could be invoked on an iframe inside a webpage that had an attribute named designMode with a value of ‘on’ in the main tag. The web may seem fast but the adoption of and creation of standards seems to move at a glacial pace sometimes so here we are 2014 and I can use Midas commands in production only because the customer set a high bar as far as browser compatibility. If you are in the same situation and want to add something like strikethrough to your htmleditor just use the following override.

Ext.define('overrides.HtmlEditor', {
   override               : 'Ext.form.field.HtmlEditor',
   enableStrikeThrough     : true,
   init                   : function () {
       var me = this;
       me.callParent(arguments);
   },

   /*
     * Called when the editor creates its toolbar. Override this method if you need to
     * add custom toolbar buttons.
     * @param {Ext.form.field.HtmlEditor} editor
     * @protected
     */

   createToolbar           : function(){
       var me = this;
       this.toolbar = Ext.widget(this.getToolbarCfg());
       if(this.enableStrikeThrough){
           /*

           https://developer.mozilla.org/en-US/docs/Midas

                 If there is no selection, the insertion point will set
                strikethrough for subsequently typed characters.
                 If there is a selection and all of the characters are
                 already struck, the strikethrough will be removed.
                 Otherwise, all selected characters
                will have a line drawn through them.
           */
           this.toolbar.add([{
               iconCls     : 'x-edit-strikethrough',
               enableToggle: true,
               handler     : function () {
                  this.relayCmd('strikethrough');
               },
               scope       : this,
               tooltip     : 'Strike Through Text'
           }]);
       }
       return this.toolbar;
   }
});

The CSS that you will need for the icon is below:


.x-edit-strikethrough {
   background: url(../images/text_strike.png) 0 0 no-repeat !important;

}

The icon itself is here : text_strike

You may now be wondering what relayCmd does and if there is some trick, I mean it can’t be this easy right? Well, what relayCmd does is takes the command s that can be found on the Mozilla website https://developer.mozilla.org/en-US/docs/Midas and their corresponding arguments and executes them. It’s that easy to add buttons and custom commands to the extjs htmleditor.

Turn your Extjs5 grid into an awesome Bar Chart with this column widget

Bar Chart column grid cell widget for Extjs 5

Ever wanted to represent a number in a grid column as a bar? Why not, doing so would have the effect of magically turning your grid into a stacked bar chart that is sortable and may contain ancillary information that is just hard to convey in a traditional grid.

Hurdles:

First, the widgets that are currently available for grid cells all try to display data that is unique to the row that it is displayed in, this makes it simpler to implement as the data is presented in the record and can be accessed much like the row data in a renderer. The problem with this widget is that it must take into account all row values (not just the current row) to be able to render the bar to the appropriate length given the range (rows).

Second, there is the issue of scaling the bars given the width of a column that is resizable.

Prerequisites:

To handle the linear scaling I relied on d3.js so you will have to add the following lines to your app.json file so that it looks like the following:

"js": [
       {
          "path": "app.js",
          "bundle": true
       },{
           "path": "resources/js/d3.min.js",
           "bundle": false
       }
   ]

Of course, this means that you went http://d3js.org/ and downloaded the minified version of d3.js to the resources/js folder in your application.

Adding your widget in Extjs5:

First create the folder structure. I like to add all my widgets in a folder called *surprise* ‘widget’ on the root of the app directory right where, say, resources would be located, so create a folder there and then go in and modify the app.json file again adding the new folder to the area where the comments indicate “Extra resources to be copied along when build”.

"resources": [       "${app.dir}/widget"   ]

Second, copy the D3GridColumnBar.js file from the D3 charts project on github (https://github.com/jmcdonald69124/ExtjsD3Charts ) The actual file is here : https://github.com/jmcdonald69124/ExtjsD3Charts/blob/master/widget/D3GridColumnBar.js

Using the new widget column in your grid:

The following section represents a grid’s columns definition incorporating the new bar widget.

columns: [
       {
           text      : 'User',
           dataIndex : 'user',
           width     : 400,
           editor: {
               xtype: 'textfield',
               allowBlank: false
           }
       },{
           text      : 'Widgets',
           dataIndex : 'total',
           sortable  : true,
           width     : 400,
           xtype     : 'widgetcolumn',
           disableTooltips: true,
           widget    : {
               xtype   : 'D3GridColumnBar'
           }
       }
   ]

The model should correctly give the type for the dataindex total in the example above as int.

Config Options:

chartBarColor : ‘hex code’ // Is the base bar color

disableTooltips: Boolean // Will remove tooltips from the bars otherwise they default to the column header text and value.

Adding grid totals to panel titles using bootstrap-ext Badges in Extjs 5’s viewModel store


Recommended Book

One of the hidden advantages to using the bootstrap-ext theme is the ability to tap into all of the components that come with Bootstrap. One of the ways that I like to highlight the number of records in a grid is a nice clean badge with the total, or better yet on a button; say an inbox button might have a nice badge that shows the number of new items. You can read more about the badge CSS here http://getbootstrap.com/components/#badges .

In Extjs5 to get the badge to show up in your Panel title from a store in the viewModel just add the following load listener and make sure the panel has a reference that you can use like the one below:

coolWidgets:{
   model   : myapp.model. CoolWidgets,
   listeners:{
       load: function(object){
          object.$binding.owner._view.lookupReference('_someCoolGrid').setTitle(' Cool Things <span class="badge">' + object.getCount() + '</span>' );
       }
   }
...

As you can see it would be just as easy to use the button’s setText() method and add the badge there.

Extjs 5 CSS Card Flip effect on dataview xtemplate


Recommended Book

The data view xtemplate in extjs is a perfect place to utilize the CSS card flip effect which allows you to place data on the back side of a div – which maximizes screen real-estate. The example below assumes that you are using the bootstrap-ext theme and that you are using extjs5. The example below works in IE10, and 11 as well as Chrome, Firefox and Safari – it does not work in older versions of IE as it relies on CSS3. The inspiration for this feature was a post by David Walsh which can be found at http://davidwalsh.name/demo/css-flip.php.

The extjs foundation:

You will need a dataview (http://docs.sencha.com/extjs/5.0/apidocs/#!/api/Ext.view.View ), as well as a store that will contain the data you wish to display. In the tpl config of the dataview you will need to add the following DIV tags at a minimum:

<div class="flip-container">
	<div class="flipper" style="display:table;" >
		<div class="front" style="display:table-cell;">

		</div>
                <div class="back" style="display:table-cell;">

                </div>
         </div>
</div>

To provide the user with a means of flipping the card you will have to add at least one element to the front and back containers that contain the class : rotate-card, an example that takes advantage of the bootstrap glyphicons and color scheme is provided below.

<div class="rotate-card circle text-center pull-right"  >
    <span class=\'rotate-card glyphicon glyphicon-file\'  ></span>
</div>

Next, because you probably want to use the required itemSelector property for something other than flipping a div around , to provide the click functionality to the a circle above listener has to be added at the dataview level, an example that matches the div tags above is below.

…
listeners:{
        afterrender   :{
            fn       : function(view){
                var someNodes = Ext.fly(view.id);
                someNodes.on('click', function(event, t) {
                    event.stopEvent();
                    Ext.fly(t).parent('.flipper').toggleCls('flip');
                }, null, {delegate: '.rotate-card'});
            }
        }
    }
…

The CSS

.flip-container{
    -webkit-flex: 1; /* Safari 6.1+ */
    -ms-flex: 1; /* IE 10 */
    flex: 1;
}
/* entire container, keeps perspective */
.flip-container {
    -webkit-perspective: 1000;
    -moz-perspective: 1000;
    -o-perspective:1000;
    -ms-perspective: 1000;
    perspective: 1000;

    -ms-transform: perspective(1000px);
    -moz-transform: perspective(1000px);
    -moz-transform-style: preserve-3d;
    -ms-transform-style: preserve-3d;

}

/* for IE*/
.flip .back {
    -webkit-transform: rotateY(0deg);
    -moz-transform: rotateY(0deg);
    -o-transform: rotateY(0deg);
    -ms-transform: rotateY(0deg);
    transform: rotateY(0deg);
}

.flip .front{
    -webkit-transform: rotateY(180deg);
    -moz-transform: rotateY(180deg);
    -o-transform: rotateY(180deg);
    transform: rotateY(180deg);
}

/* END: for IE */

.flipper {
    -webkit-transition: 1.0s;
    -webkit-transform-style: preserve-3d;
    -ms-transition: 1.0s;

    -moz-transition: 1.0s;
    -moz-transform: perspective(1000px);
    -moz-transform-style: preserve-3d;
    -ms-transform-style: preserve-3d;

    transition: 1.0s;
    transform-style: preserve-3d;

    position: relative;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    -webkit-transition: all 1.0s ease-in-out;
    -moz-transition: all 1.0s ease-in-out;
    -o-transition: all 1.0s ease-in-out;
}

.front, .back {
    -webkit-backface-visibility: hidden;
    -moz-backface-visibility: hidden;
    -ms-backface-visibility: hidden;
    backface-visibility: hidden;

    -webkit-transition: 0.6s;
    -webkit-transform-style: preserve-3d;

    -moz-transition: 0.6s;
    -moz-transform-style: preserve-3d;

    -o-transition: 0.6s;
    -o-transform-style: preserve-3d;

    -ms-transition: 0.6s;
    -ms-transform-style: preserve-3d;

    transition: 0.6s;
    transform-style: preserve-3d;

    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;

}

.front {
    -webkit-transform   : rotateY(0deg);
    -ms-transform       : rotateY(0deg);
    background-position : center center;
    z-index             : 2;

}

.back {
    -webkit-transform: rotateY(-180deg);
    -moz-transform: rotateY(-180deg);
    -o-transform: rotateY(-180deg);
    -ms-transform: rotateY(180deg);
    transform: rotateY(-180deg);
}
/*
.rotate-3d {
    -webkit-transform: rotateY(180deg);
    -moz-transform: rotateY(180deg);
    transform: rotateY(180deg);
}*/

.circle {
    border-radius: 50%;
    width: 30px;
    background-color: #5cb85c;
    border: 1px solid #52A052;
    height: 30px;
    cursor: pointer;
}

.circle .glyphicon{
    color: #FFF;
    font-size: 15px;
    padding: 6px 0px 0px 3px;
}

.circle:hover{
    background-color: #52A052;
}

.front  {position: relative;}
.back       {position: absolute; top: 0; left: 0; width: 100%; height: 100%}

The complete dataview example is below:

{
    xtype       : 'dataview',
    flex        : 1,
    itemSelector: 'span.glyphicon-trash',
    bind :{
        store : '{cars}'
    },
    listeners:{
        itemclick   : {
            fn      : 'onDeleteFastCar',
            scope   : 'controller'
        },
        afterrender   :{
            fn       : function(view){
                var someNodes = Ext.fly(view.id);
                someNodes.on('click', function(event, t) {
                    event.stopEvent();
                    Ext.fly(t).parent('.flipper').toggleCls('flip');
                }, null, {delegate: '.rotate-card'});
            }
        }
    },
    emptyText   : '<div style="width:95%" class="center-block"><div class="alert alert-warning" role="alert"><strong>There are no fast cars here.</strong></div></div>',
    autoScroll  : true,
    tpl : [
        '<div style="width:100%; padding:5px;">',
        '<tpl for=".">',
            // Required outer container of the card object
            '<div class="flip-container">',
            // Required
            '<div class="flipper" style="display:table;" >',

            // Front card information, this is visible when the
            // dataview is populated.

            '<div class="front" style="display:table-cell;">',
                    // Using the Bootstrap panel that is provided in the bootstrap-ext theme
                    '<div class="panel panel-default" > ',
                        '<div class="panel-heading"><h4>{car_model} / {car}</h4></div>',
                        '<div class="panel-body">',
                                    '{car_description}</br>',
                            // Required tag with the class 'rotate-card' so
                            // that the user can click and change from front to
                            // back.
                            '<div class="rotate-card circle text-center pull-right"  >',
                                '<span class=\'rotate-card glyphicon glyphicon-file\'  ></span>',
                            '</div>',
                        '</div>',
                    '</div>',
            '</div>',

            // Back card information, this is visible when the
            // dataview is populated.

            '<div class="back" style="display:table-cell;">',
            '<div class="panel panel-default" > ',
                    '<div class="panel-heading">',
                        '<h4>Models in Stock Near You,
                            '<div class="pull-right">',
                                '<span class=\'glyphicon glyphicon-trash\' style=\'cursor:pointer;color:#FFF;padding-right:5px;\'></span>',
                            '</div>',
                        '</h4>',
                    '</div>',
                    '<div class="panel-body">',
                        '<strong>{location} :</strong>{vin_number}</br>' ,
                        // Required tag with the class 'rotate-card' so
                        // that the user can click and change from front to
                        // back.
                        '<div class="rotate-card circle text-center pull-right"  >',
                            '<span class=\'rotate-card glyphicon glyphicon-retweet\'  ></span>',
                        '</div>',
                    '</div>',
            '</div>',
            '</div>',
        '</div>',
        '</div>',
        '</tpl>',
        '</div>'
    ]
}