Adding UIkit to Pinegrow: Pt 3
Namaste!
In this part of the tutorial, we will discuss the overall structure of our plugin, outline the basic parts of a Pinegrow component, and then finally add our first component! Just to recap, we have set up our plugin project with a custom folder structure, added a custom template with JSON instructions for Pinegrow, added in Sass files, and added the main JavaScript to initialize our plugin. Whew, I’m tired just reading that. So the next step is to figure out what from UIkit needs to be implemented and how.
Looking at the UIkit docs there is a long list of components (click image to show a partial list). However, when we drill down, some are truly HTML components, like the ‘Article’ component, while others are classes like ‘uk-height-1-1
‘ or 'uk-width-3-4
‘, that can be added to multiple different HTML elements in different contexts. So, for each of the HTML components, we will add them to our framework. For all of the non-HTML components of the UIkit framework, we can either add them as options for each tag, like ‘<div>
‘ or ‘<span>
‘, expect the user to know enough about the UIkit framework to add them manually, or set-up a global selection like many of the other Frameworks implement.
“Enough of the theoretical, let’s code!“, I can hear you shouting at your computing device. “Not so fast!“, I reply. Let’s run through the basic steps for adding an HTML component (really any component) to Pinegrow. From an API standpoint, Pinegrow is a joy to work with. You can throw pretty much any JS into your plugin file as long as it returns an object for Pinegrow to work with at the end. This means that the style you use to create these objects can vary widely – you can be all OOP, or write a bunch of singletons. For the purposes of this tutorial, I’m not going to try and optimize my JS, I’m just going to write readable code.
There are essentially three steps to adding a section of HTML code to our Pinegrow plugin – it is actually more like five steps, but the other two involve creating a section to display our code snippet. So what are those essential steps?
- Create a new object using the ‘
PgComponentType
‘ constructor and populate that object with all our super fun deets. - Register that new object with our plugin framework using the ‘
addComponentType
‘ function. - Add our component to a section using the ‘
setComponentTypes
‘ function.
So, what are the other two steps?
- Create a section object using the ‘
PgFrameworkLibSection
‘ constructor. - Let Pinegrow know about that section using the ‘
addLibSection
‘ function.
I know what you are saying right now. “Thanks, Bo! That is as clear as the time I tried snorkeling through the mud.” Well, I guess we ought to flash some pretty pictures and actual end product.
Let’s take the iconic Bootstrap Jumbotron as an example. Pulling up a starter template in Pinegrow, we can see it in all its glory at the top.
First off, looking at the list (image to the left), we can see a ton of drag-and-drop elements for us to add to our page. Scrolling down, we find a section labeled ‘NAVIGATION bootstrap 4’, and within it, a small button labeled ‘Jumbotron’. So going back to the steps above, the last two steps create the ‘NAVIGATION’ section. The first three steps create the ‘Jumbotron’ button and associated code, and then add it to the ‘NAVIGATION’ section. A little clearer?
If we then select the Jumbotron element on the page we can see the available options listed in the “Properties” tab (image to the right. Some of these are specific to the Jumbotron, while some of the others are general page settings. The Pinegrow API gives us a lot of control when to display these potential properties depending on the element that is selected on the page. As we will detail later, these components are identified by either abbreviated or full-length JS queries.
Okay, let’s start writing our first component. Open up your JS file that we created in the last tutorial. We want to start adding code before the anonymous function, but after we assigned our template to the framework, staying within the closing brace of our ‘createUikitPlugin
‘ function. For our first component, we will pick a somewhat simple one – the Alert component. Pulling up the documentation page there is the basic Alert code with four style modifiers, plus an additional close element. We won’t worry about the other component options for the moment.
So from the list above, our first step is to create a new ‘PgComponentType’ object.
var uikit_alert = new PgComponentType("ui-alert", "Alert", {});
So this line of code assigns the new, ‘PgComponentType
‘ object to ‘uikit_alert
‘. It also passes in what that component type is – ‘ui-alert
‘, and a name for that type – ‘Alert
‘. The third parameter that can be passed to ‘PgComponentType’ when creating a new object is ‘data’ as the third parameter. In this case, we are passing an empty object ‘{}
‘, which we will fill up in a moment. A quick note about naming. For all my variables I’m choosing to prefix them with ‘uikit_’. Because we are declaring them within our function they should be scoped to that function and not conflict with other plugins/sections of code. Therefore, they just have to be unique in our plugin, but prefixing doesn’t hurt! The component type, however, does have to be unique, so I am prefixing it with ‘ui-‘. This will keep our type components distinguishable from our variables. Unlike variables, you can use dashes in your type name.
On to step two:
framework.addComponentType(uikit_alert);
This line of code calls the Pinegrow function ‘addComponentType’, passing in the component type as an argument. This essentially changes our framework object to now include this component type.
Okay, let’s start with our actual alert object data. Again, there are a number of ways that we can accomplish this and no one is more correct than another. I’ll show you the way I prefer and an alternative. There are two key:value pairs that are required for each component object: ‘selector
‘ and ‘code
‘. The alternative way of adding these first:
uikit_alert.selector = "[uk-alert]";
uikit_alert.code = "<div uk-alert>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div>";
All this code does is add the two keys, along with values to our ‘uikit_alert
‘ object. This way works perfectly well, I just don’t like typing out ‘uikit_alert.
‘ each time. Instead, I like to build the object up within the original call for a new object, like so:
var uikit_alert = new PgComponentType("ui-alert", "Alert", {
selector: "[uk-alert]",
code:
"<div uk-alert>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div>",
tags: "major",
});
In this case the key: value pairs are put in with the key not in quotes, followed by a colon, followed by the value, typically enclosed within quotes. I tend to favor single quotes around my values because it means less escaping of characters in my ‘code’ block. However, the code linter/beautifier I’m using at the moment prefers double quotes and I don’t feel like fighting it.
So now that we have seen the ‘how’, what about the ‘what’? What exactly are these keys doing? The ‘selector’ key delineates how Pinegrow should identify our particular component within the HTML code. In this case, the value is, ‘[uk-alert]
‘. This means that Pinegrow should identify our block of code by the presence of the ‘uk-alert
‘ attribute. This is just like targeting in jQuery/JS.
The next key:value pair is ‘code
‘ along with the actual HTML coding for the element we wish to add. Finally, notice that I snuck another key:value pair in within this code: 'tags
‘. This notifies Pinegrow that it is an element that should be highlighted within the tree. This isn’t required and as we will see with the next components, sometimes not wanted.
Next, we need to add these four modifiers as add-on classes for our alerts. This is accomplished by creating a section of actions within our object.
sections: {
ui_alert_options: {
name: "Alert Options",
fields: {
[...]
},
},
},
The ‘sections
‘ key is what kicks this all off. Each of the sections are then part of an object that is stored under this key. Sometimes it feels like “Inception“! Our first (and only section) is given the key ‘ui_alert_options
‘ and itself holds (wait for it) another object. Each of these sections should have a unique name. I’m accomplishing this by prefixing them with ‘ui_’ and adding a descriptor. This object typically contains two key:value pairs, but in complicated cases can contain more. Those two keys are ‘name
‘ and ‘fields
‘. The name is displayed in the properties section when the ‘Alert
‘ element is selected. The fields are yet another object that defines the items we want to be displayed in the ‘Alert Options’ section. What do those fields look like?
At the simplest, these fields are checkboxes, drop-down select menus or text fields. Typically they perform one of two actions – add/remove a class or add/remove/modify an attribute on the selected element. For this example, we will look at using the drop-down menu to add one of the four UIkit modifying classes to our alert.
ui_alert_types: {
type: "select",
name: "Alert Types",
action: "apply_class",
show_empty: true,
options: [
{
key: "uk-alert-default",
name: "Default",
},
{
key: "uk-alert-primary",
name: "Primary",
},
{
key: "uk-alert-secondary",
name: "Secondary",
},
{
key: "uk-alert-danger",
name: "Danger",
},
],
},
The first key, ‘select'
tells Pinegrow that we want to add a drop-down menu. The next key, ‘name
‘, gives a display name for the menu. The ‘action
‘ key tells Pinegrow what should occur when the user makes a selection. In this case, it should change the class on the selected element. Skipping the next key, the next required key for a ‘type: "select"
‘ section are the ‘options
‘ that will be available. These are passed into the program within an array of objects (note the brackets rather than braces following the ‘option
‘ key). Each of the objects in this array are pairs of the class that should be added, denoted by the ‘key
‘ key, and what the selection should be called in the dropdown denoted by the ‘name
‘ key.
Going back to the skipped key, ‘show_empty’, we want the user to have the option to not add any modifying class, so we set this equal to true – without surrounding quotes. The same would be true with false.
Hang in there, troopers! We are almost done and I’ll leave you some code to ponder and some pretty pictures, soon!
So, we have covered the first two steps for creating a new component, now we need to add them into the display loop. Here is how we accomplish this:
var ui_alert_section = new PgFrameworkLibSection("ui-alerts-section", "Alerts");
ui_alert_section.setComponentTypes([
uikit_alert,
]);
framework.addLibSection(ui_alert_section);
Like with the component, we create a new object using the ‘PgFrameworkLibSection()’ constructor and assign it to a variable. I have elected (for code readability) to use a unique variable for each section, but this isn’t actually necessary. I could reuse the same variable and save some code bloat. For example, the image at the right is taken from the “materialize.js” file. The object is initialized with a type name – ‘ui-alerts-section
‘ and a name for Pinegrow to display on the section – ‘Alerts
‘. The type should be selected not to conflict, so I’ve prefixed it with ‘ui-‘. I’ve also added section at the end just for clarity of code.
Next, we pass in an array of all of the components that we want to be displayed in this section as an argument to the ‘setComponentTypes’ function. Finally, we pass that object to our framework using the ‘addLibSection
‘ function and our new object as an argument. WooHoo! We did it. Our first component!
I’ve gone ahead and added two more alert components, a close button and a close link. I’ll show the code and then we can look at some screenshots in pine grow. (Note: this code may look a little different than the individual snippets because of how my code editor is acting up, grrrr!!!)
//Alerts ******************************
//Alert
var uikit_alert = new PgComponentType("ui-alert", "Alert", {
selector: "[uk-alert]",
tags: "major",
code:
"<div uk-alert>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div>",
sections: {
ui_alert_options: {
name: "Alert Options",
fields: {
ui_alert_types: {
type: "select",
name: "Alert Types",
action: "apply_class",
show_empty: true,
options: [
{
key: "uk-alert-default",
name: "Default"
},
{
key: "uk-alert-primary",
name: "Primary"
},
{
key: "uk-alert-secondary",
name: "Secondary"
},
{
key: "uk-alert-danger",
name: "Danger"
},
],
},
},
},
},
});
framework.addComponentType(uikit_alert);
//Alert close button
var uikit_alert_close_button = new PgComponentType(
"ui-alert-button",
"Alert Close Button",
{
selector: "button.uk-alert-close",
code: '<button class="uk-alert-close">Close</button>',
sections: {
ui_alert_close_button: {
name: "Close Options",
fields: {
add_icon_button: {
type: "checkbox",
name: "Add close icon?",
action: "element_attribute",
attribute: "uk-close",
empty_attribute: true,
value: "1",
},
make_icon_button_large: {
type: "checkbox",
name: "Make icon larger?",
action: "apply_class",
value: "uk-close-large",
},
},
},
},
},
);
framework.addComponentType(uikit_alert_close_button);
//Alert close link
var uikit_alert_close_link = new PgComponentType(
"ui-alert-link",
"Alert Close Link",
{
selector: "a.uk-alert-close",
code: '<a class="uk-alert-close">Close</a>',
sections: {
ui_alert_close_link: {
name: "Close Options",
fields: {
add_icon_link: {
type: "checkbox",
name: "Add close icon?",
action: "element_attribute",
attribute: "uk-close",
empty_attribute: true,
value: "1",
},
make_icon_link_large: {
type: "checkbox",
name: "Make icon larger?",
action: "apply_class",
value: "uk-close-large",
},
},
},
},
},
);
framework.addComponentType(uikit_alert_close_link);
var ui_alert_section = new PgFrameworkLibSection("ui-alerts-section", "Alerts");
ui_alert_section.setComponentTypes([
uikit_alert,
uikit_alert_close_button,
uikit_alert_close_link,
]);
framework.addLibSection(ui_alert_section);
ย ย I will leave it to you, dear reader, to puzzle over the two other sections of code that add the close button and the close link. In another tutorial, we will cover the use of both the ‘checkbox’ and ‘text’ types. We will also cover more complex selectors and some helper functions for the components that get added to multiple page elements. Until then, feel free to reach out in the comments or by e-mail with questions and comments good or bad!!
Metta!!
Update: I have now finished a full-length version of the UIkit plugin for Pinegrow. Purchase of this plugin not only adds the UIkit framework to Pinegrow for unlimited sites, but it also gives you access to the completed code for learning purposes! As a bonus, I’ll give you access to a Facebook page to discuss coding for Pinegrow. Click here to learn more!!
hi, me again ๐
In the above code snippet, we have
uikit_alert.selector = “[uk-alert]”;
uikit_alert.code = “<div uk-alert>
should this be ui-alert?
Gutenturd spell check gone amok?
Nope, it should be ‘uk’. For the plugin I’m prefixing with either the entire framework name, ‘uikit’, or with ‘ui’ for short. The actual framework uses ‘uk’. I just wanted to make sure that my JS didn’t have any conflicts. Hope that isn’t too confusing! ๐
All good!
Ta for the clarification
๐
I shall explore all of this again in a little while as It wasn’t quite smooth sailing.
I got no further than this part, so shall see if I can figure out my error.
ciao for now
๐
If you describe to me the error you are getting, maybe I can help. There are a lot of moving parts to the plugin, even at this early stage. It is likely something that is easily corrected.
I’m not near smart enough to understand the coding but love to support such efforts so be sure to let me know about the early launch of your plugin.
Aww, Les! Don’t be so doubtful about yourself! If this donkey can do it so could you. As an aside, plugin is finished and should be released very soon!