Monday, March 31, 2014

Using MomentJS to Extend the jQuery UI TimePicker Spinner Widget

In my last post, I used several jQuery UI Spinner widgets to create a Time Picker widget. At the time, I was more interested in making the widgets work together than providing a robust getter/setter function. So I cobbled together a simple string parser to split up the time value and set each spinner to the desired value. Additionally, the widget assumed the input format was a 12-hour clock which is not always desirable. So, in this post, I'll add some more features to provide more flexibility and options related to the format. While I don't like adding too many external dependencies, the MomentJS library does everything I need plus a pile of other great features. I generally use it in every project that has any kind of date/time display, manipulation, entry, etc. That said, I made an effort to gracefully fallback to the simple string method I originally wrote for anyone who doesn't want the extra 9Kb of gzip'd Javascript loading.

To review, the current TimePicker has this implementation for getting the current value of the widget:

      _value: function() {
         var hour = this.$hour.val(),
             min = this.$minute.val(),
             ampm = this.$ampm.val();

         return hour + ':' + min + ' ' + ampm;

And this chunk of code handles parsing an incoming value and setting each spinner:

      _parse: function( val ) {
         var parts = val.split( /[: ]/ ),
             hour, min, ampm;

         if ( parts.length < 2 ) return;

         hour = parts[0];
         min = parts[1];
         ampm = parts[2];

         this.$hour.paddedspinner( 'value', +hour );
         this.$minute.paddedspinner( 'value', +min );
         this.$ampm.ampmspinner( 'value', ampm == 'AM' ? 0 : 1 );


The goal is to retain those sections of code but 1) handle a 24-hour clock and 2) detect Moment and use it to both parse the incoming value or create a Moment instance to return the current value of the widget. Before doing that, we need to know the format to use for the widget. I decided to use a format string that aligns with Moment to determine whether to use a 12 or 24 hour clock:

Saturday, March 22, 2014

Build a Time Picker using jQuery UI Spinner Widgets

I've tried several methods of allowing a user to enter time. I prefer solutions that ensure the user can not enter an invalid time. A free-form text box with a mask will work to guide the entry but the validation is more complicated - especially if you're trying to restrict the entry to specific time intervals (ie every 5 minutes). In situations where there is an allowed time interval, I provided a single drop-down with all the valid selections. However, for small time intervals, the select box becomes large and difficult to navigate. In those cases, I've used several select boxes, each representing a different component of the time. The jQuery UI Spinner demo uses a single field to allow spinning through components of the entered time. However, there is no guided free-form entry so you'll have to validate the entry and ensure it is actually correct. I also found a time picker that extends the jQuery UI Datepicker widget. It has a lot of options for controlling the input a user interacts with while entering time. When creating a widget to enter different time components, I prefer to keep the input visually similar to how time is actually displayed. I think its more intuitive and natural since the whole value can be seen in its normal form. Given that, I decided to assemble several spinners to create a time selection widget. While the widget I present here will work, it will not have many features. However, there are three aspects of the functionality I'd like to focus on providing:

  • A value method to get/set the time in the widget. Since the value is split across three spinners, it needs to be parsed and formatted appropriately
  • Minimally, the minutes should be zero padded for anything less than 10 and you should only be able to switch between AM and PM
  • When you reach the limit of the hours or minutes, roll over to the beginning/end and change the other spinners. For example, if you go from 11 to 12, switch the AM/PM choice to allow you to move through the entire 24 hour day without having to change both fields individually.

Monday, March 10, 2014

Drawing Shapes with CSS3 Box Shadow

When I first looked at the box shadow style, I did not really give much thought to the fact you could specify multiple shadows. At least not until I stumbled upon this pixel art creator which used that ability to build pixel art in a single tag. So, of course I drew something and started thinking what else I could do with this interesting property. It meant that I could add more complex shapes to my content where I might otherwise have to use a graphic. I started with a comment bubble:

Which scaled down works nicely as an icon:

And the CSS to create it is probably smaller than an image or font file:

<div style="
  height: 1px; width: 1px; 
  box-shadow: 2px 3px 0 #3498DB,3px 3px 0 #3498DB,
              4px 3px 0 #3498DB,5px 3px 0 #3498DB,
              6px 3px 0 #3498DB,7px 3px 0 #3498DB,
              8px 3px 0 #3498DB,9px 3px 0 #3498DB,
              10px 3px 0 #3498DB,11px 3px 0 #3498DB,
              1px 4px 0 #3498DB,2px 4px 0 #3498DB,
              3px 4px 0 #3498DB,4px 4px 0 #3498DB,
              5px 4px 0 #3498DB,6px 4px 0 #3498DB,
              7px 4px 0 #3498DB,8px 4px 0 #3498DB,
              9px 4px 0 #3498DB,10px 4px 0 #3498DB,
              11px 4px 0 #3498DB,12px 4px 0 #3498DB,
              1px 5px 0 #3498DB,2px 5px 0 #3498DB,
              3px 5px 0 #3498DB,4px 5px 0 #3498DB,
              11px 5px 0 #3498DB,12px 5px 0 #3498DB,
              1px 6px 0 #3498DB,2px 6px 0 #3498DB,
              3px 6px 0 #3498DB,4px 6px 0 #3498DB,
              5px 6px 0 #3498DB,6px 6px 0 #3498DB,
              7px 6px 0 #3498DB,8px 6px 0 #3498DB,
              9px 6px 0 #3498DB,10px 6px 0 #3498DB,
              11px 6px 0 #3498DB,12px 6px 0 #3498DB,
              1px 7px 0 #3498DB,2px 7px 0 #3498DB,
              9px 7px 0 #3498DB,10px 7px 0 #3498DB,
              11px 7px 0 #3498DB,12px 7px 0 #3498DB,
              1px 8px 0 #3498DB,2px 8px 0 #3498DB,
              3px 8px 0 #3498DB,4px 8px 0 #3498DB,
              5px 8px 0 #3498DB,6px 8px 0 #3498DB,
              7px 8px 0 #3498DB,8px 8px 0 #3498DB,
              9px 8px 0 #3498DB,10px 8px 0 #3498DB,
              11px 8px 0 #3498DB,12px 8px 0 #3498DB,
              1px 9px 0 #3498DB,2px 9px 0 #3498DB,
              3px 9px 0 #3498DB,12px 9px 0 #3498DB,
              2px 10px 0 #3498DB,3px 10px 0 #3498DB,
              4px 10px 0 #3498DB,5px 10px 0 #3498DB,
              6px 10px 0 #3498DB,7px 10px 0 #3498DB,
              8px 10px 0 #3498DB,9px 10px 0 #3498DB,
              10px 10px 0 #3498DB,11px 10px 0 #3498DB,
              3px 11px 0 #3498DB,4px 11px 0 #3498DB,
              3px 12px 0 #3498DB;"

And then it seemed only natural to make a spinner:

@keyframes spin {
    from { transform: translate3d(0, 0, 0) rotate(0deg); }
    to { transform: translate3d(0, 0, 0) rotate(360deg); }

.spinner {
    height: 1px; 
    width: 1px;
    transform-origin: 14px 14px;
    animation-name: spin; 
    animation-duration: 1s;
    animation-iteration-count: infinite; 
    animation-timing-function: linear;    

.spinner.balls {
       13px 2px 0 #8F8588,14px 2px 0 #8F8588,12px 3px 0 #8F8588,
       13px 3px 0 #8F8588,14px 3px 0 #8F8588,15px 3px 0 #8F8588,
       12px 4px 0 #8F8588,13px 4px 0 #8F8588,14px 4px 0 #8F8588,
       15px 4px 0 #8F8588,6px 5px 0 #8F8588,7px 5px 0 #8F8588,
       13px 5px 0 #8F8588,14px 5px 0 #8F8588,20px 5px 0 #8F8588,
       21px 5px 0 #8F8588,5px 6px 0 #8F8588,6px 6px 0 #8F8588,
       7px 6px 0 #8F8588,8px 6px 0 #8F8588,19px 6px 0 #8F8588,
       20px 6px 0 #8F8588,21px 6px 0 #8F8588,22px 6px 0 #8F8588,
       5px 7px 0 #8F8588,6px 7px 0 #8F8588,7px 7px 0 #8F8588,
       8px 7px 0 #8F8588,19px 7px 0 #8F8588,20px 7px 0 #8F8588,
       21px 7px 0 #8F8588,22px 7px 0 #8F8588,6px 8px 0 #8F8588,
       7px 8px 0 #8F8588,20px 8px 0 #8F8588,21px 8px 0 #8F8588,
       3px 12px 0 #8F8588,4px 12px 0 #8F8588,23px 12px 0 #8F8588,
       24px 12px 0 #8F8588,2px 13px 0 #8F8588,3px 13px 0 #8F8588,
       4px 13px 0 #8F8588,5px 13px 0 #8F8588,22px 13px 0 #8F8588,
       23px 13px 0 #8F8588,24px 13px 0 #8F8588,25px 13px 0 #8F8588,
       2px 14px 0 #8F8588,3px 14px 0 #8F8588,4px 14px 0 #8F8588,
       5px 14px 0 #8F8588,22px 14px 0 #8F8588,23px 14px 0 #8F8588,
       24px 14px 0 #8F8588,25px 14px 0 #8F8588,3px 15px 0 #8F8588,
       4px 15px 0 #8F8588,23px 15px 0 #8F8588,24px 15px 0 #8F8588,
       6px 19px 0 #8F8588,7px 19px 0 #8F8588,20px 19px 0 #8F8588,
       21px 19px 0 #8F8588,5px 20px 0 #8F8588,6px 20px 0 #8F8588,
       7px 20px 0 #8F8588,8px 20px 0 #8F8588,19px 20px 0 #8F8588,
       20px 20px 0 #8F8588,21px 20px 0 #8F8588,22px 20px 0 #8F8588,
       5px 21px 0 #8F8588,6px 21px 0 #8F8588,7px 21px 0 #8F8588,
       8px 21px 0 #8F8588,19px 21px 0 #8F8588,20px 21px 0 #8F8588,
       21px 21px 0 #8F8588,22px 21px 0 #8F8588,6px 22px 0 #8F8588,
       7px 22px 0 #8F8588,13px 22px 0 #8F8588,14px 22px 0 #8F8588,
       20px 22px 0 #8F8588,21px 22px 0 #8F8588,12px 23px 0 #8F8588,
       13px 23px 0 #8F8588,14px 23px 0 #8F8588,15px 23px 0 #8F8588,
       12px 24px 0 #8F8588,13px 24px 0 #8F8588,14px 24px 0 #8F8588,
       15px 24px 0 #8F8588,13px 25px 0 #8F8588,14px 25px 0 #8F8588;

Its important to shift the transform origin to match the final size of your box shadow "grid". The DIV is only 1 pixel high and wide so the point of rotation is in the top, left corner of the box shadow area.

While probably something that should be used in moderation, its still a fun trick that can come in handy in certain situations.

Wednesday, March 5, 2014

Customize the jQuery UI AutoComplete Drop Down Select Menu

When attempting to customize the jQuery UI Autocomplete widget's select box markup to match a Bootstrap drop down menu, I found that the hooks available are not part of the normal options hash. It seemed a little odd to provide this functionality in the form of a plugin extension and the documentation doesn't provide quite enough context for someone less familiar with the internals of the core widget library to actually implement the hook. The primary functions of interest are _renderItem and _renderMenu. Since they are prefixed with the "_", the widget factory will hide these methods from the plugin. So, in the documentation, you can see there is a method "close" which is called through the plugin on the target element:

   $( '#myac' ).autocomplete( 'close' )

however, you can't call _renderMenu in the same way:

   $( '#myac' ).autocomplete( '_renderMenu' )

That will throw an error. However, you can access anything in the widget instance object via $.data:

   var ac_inst = $( '#myac' ).data( 'ui-autocomplete' );

Now you can do fancy things with this object like change the behavior of _renderMenu: