Friday, March 2, 2018

APEX 5.2 Page Designer Style for APEX 5.1

Have you been playing, trying and testing stuff on the Early Adapter of APEX 5.2.
If you haven't, it's still available (as of writing this) for you to try at apexea.oracle.com.

If you have, you probably noticed that the user interface of the page designer was refined and is looking better than ever.

APEX 5.2 Page Designer:

APEX 5.1 Page Designer:


After a while, you'll get use to the new look of the page designer and going back to 5.1 might feel weird.

Wouldn't it be nice if we could have the same look and feel on APEX 5.1.

We can actually achieve that and it's not that difficult.

The following is relying on CSS only, no JavaScript required. So there's no risk of breaking the page designer or any of its functionalities.

So how can we do it?

There's a super useful extension I used: Stylish
It's available on Chrome, Firefox and Opera.

What it does is inject CSS for whatever website your tell it to.

How to set everything up:
Step 1. Install the extension

Step 2. Create a new style

Step 3. Copy and paste the following code
/*-------------------------------------
 * APEX 18.1 Page Designer Style for APEX 5.1
 * Version: 1.1 (2018-03-16)
 * Author:  Maxime Tremblay
 *
 * https://max-tremblay.blogspot.ca/2018/03/apex-52-page-designer-style-for-apex-51.html
 *
 * To be used with the Stylish Addon: https://userstyles.org/
 *  - Chrome:  https://chrome.google.com/webstore/detail/stylish-custom-themes-for/fjnbnpbmkenffdnngjfgmeleoegfcffe
 *  - Firefox: https://addons.mozilla.org/en-US/firefox/addon/stylish/
 *  - Opera:   https://addons.opera.com/en-gb/extensions/details/stylish/
 *
 * Use with regexp URL: https?://(?:(?!apexea.oracle.com).)*f\?p=4000:4500.*
 * or any other URL you wish
 *
 * Using Gist: https://gist.github.com/maxime-tremblay/59c46c9f441801b6de0c88f72d0d63d9
 * Served using: https://rawgit.com/
 *
 *-------------------------------------
 */

/* You can use the rawgit URL or copy everything from the Gist */
@import url('https://rawgit.com/maxime-tremblay/59c46c9f441801b6de0c88f72d0d63d9/raw/aa9877dad692317aa676b63911b6366631ee772d/APEX18.1-Page_Designer_for_APEX5.1.css');

Step 4. Define what website it should be applied to.

Since I'm working with APEX 5.1 everywhere, I'm using the regular expression  https?://(?:(?!apexea.oracle.com).)*f\?p=4000:4500.* to apply it everywhere except apexea.oracle.com.


Here's the end result:

Of course not everything is the exact same. Everything that relies on new CSS classes or anything new will obviously not be replicated.

Here's a list of small differences
  • Never element are not strikethrough
  • Elements with unsaved changes don't have the left border highlight
  • The property editor header is looking differently (buttons removed in 5.2 and search filter moved).

I've been using this for a couple of weeks now and I have to say that I really love it.

Enjoy!

Thursday, March 1, 2018

Datepicker Customization

Back in 2010, the Oracle Application Express release 4.0 included the jQuery JavaScript library as well as the jQuery UI JavaScript library. With it came the new revision of the datepicker item based on the jQuery UI's widget as we have it today. Most of the widget's options are available to us as item attributes.
But, there might come a time when you get asked or need to change some option that is not available.

Maybe you need to change the first day of the week to Monday instead of Sunday, or need to disable some days, etc.

For a list of all available options and the utility functions, you should have a look at the Datepicker Widget API Documentation.

There are a couple of nice hidden features that you might not know existed.
Let's have a look at a couple of them.

Changing the first day of the week to Mondays
$('#P1000_DATE_3').datepicker("option", "firstDay", 1);

Disabling the selection of weekends using built-in utility function.
$('#P1000_DATE_4').datepicker("option", "beforeShowDay", $.datepicker.noWeekends);

Disabling the selection of Mondays
function disableMondays(pDate){
    var lTooltipDate = "Tooltip for disabled dates";
    if (pDate.getDay() === 1) {
        return [false, 'disabledDayClass', lTooltipDate];
    }
    else {
        return [true, null, null];
    }
}

$('#P1000_DATE_5').datepicker("option", "beforeShowDay", function(date) {return disableMondays(date);});

However, if you change an option for a datepicker item, you'll notice that the icon trigger will look different.



So what's going on exactly?
Let's have a look at the html.

Before changing an option:
<input type="text" class="datepicker apex-item-text apex-item-datepicker hasDatepicker" id="P1000_DATE_1" name="P1000_DATE_1" maxlength="" size="30" value="" autocomplete="off">
<button type="button" class="ui-datepicker-trigger a-Button a-Button--calendar">
    <span class="a-Icon icon-calendar"></span>
    <span class="u-VisuallyHidden">Popup Calendar: Date<span></span></span>
</button>

After changing an option:
<input type="text" class="datepicker apex-item-text apex-item-datepicker hasDatepicker" id="P1000_DATE_1" name="P1000_DATE_1" maxlength="" size="30" value="" autocomplete="off">
<button type="button" class="ui-datepicker-trigger">
    <span class="a-Icon icon-calendar"></span>
    <span class="u-VisuallyHidden">Popup Calendar: Date<span></span></span>
</button>


We can see that the button element is missing two APEX classes after an option is changed:
a-Button a-Button--calendar

So, to fix the issue with the button trigger we simply need to add the classes back.
That's also exactly what APEX is doing on page load for each datepicker item.
(function() {
    apex.widget.datepicker("#P1000_DATE_1", {
        "buttonImageOnly": false,
        "buttonText": "\u003Cspan class=\u0022a-Icon icon-calendar\u0022\u003E\u003C\u002Fspan\u003E\u003Cspan class=\u0022u-VisuallyHidden\u0022\u003EPopup Calendar: Date\u003Cspan\u003E",
        "showTime": false,
        "defaultDate": new Date(2018, 1, 26, 20, 32, 47),
        "showOn": "button",
        "showOtherMonths": false,
        "changeMonth": false,
        "changeYear": false
    }, "dd-M-y", "en");
    apex.jQuery('#P1000_DATE_1').next('button').addClass('a-Button a-Button--calendar');
})();

Having a look again at our first example (changing the first day of the week to Mondays). We should use something like this.
$('#P1000_DATE_3').datepicker("option", "firstDay", 1).next('button').addClass('a-Button a-Button--calendar');
Now, what if you would like to change an option for every datepicker in your application.

Once a datepicker is initialized a "hasDatepicker" class is added to the element. So instead of using the item ID as the jQuery selector, we could use that class and have the code execute on page load.
$('.hasDatepicker').datepicker("option", "firstDay", 1).next('button').addClass('a-Button a-Button--calendar');

If the previous code is not working as expected, it my be due to a timing issue. Since the code to customize the datepicker items is executed on page load and that the dapicker are initialized on page load also. The customizing code could be running before the actual initialization. To fix that you can add a short delay to it.

You can have a look at it in action in my Demo Application

Enjoy!