Retina Icon Picker

“This life’s hard, but it’s harder if you’re stupid.”
George V. Higgins

Nothing is ever easy. Oh sure, it all seems easy in the beginning, but then you get into it and you realize that things are just a little more complicated than they first appeared. It’s always something. But then, as my old boss used to explain to me, if it was easy, then anyone could do it!

All I really wanted to do was to create a SELECT statement with an option list of Retina Icons that included the icon image itself in the drop-down selection list. How hard could that be? Something that might come out looking somewhat like this:

SELECT statement with icon images in the option list

Unfortunately, the current SELECT statement doesn’t support that where SIZE is not greater than 1. I have no idea why it is that there is that distinction, but on a single line SELECT statement, it ignores the CSS for the icon. However, once you add SIZE=”2″ or something else other than 1, it works great. That’s really nice, but that’s not what I was after. I didn’t want to take up more than one line on my form for this, so after searching fruitlessly for a way to get around this annoyance, I finally accepted the reality that I was going to need to look for something else.

Eventually, I decided to stick with the SELECT, but put it in a modal pop-up that could show a dozen or so lines when needed, and then just go away when I was done making my selection. That seemed easy enough. How hard could it be?

To start out, I copied all of the values from that master icon page and used them to create an array of all of the options.

data.option = [
	{"label": "accessibility", "value": "accessibility"},
	{"label": "activity-circle", "value": "activity-circle"},
	{"label": "activity-stream", "value": "activity-stream"},
	{"label": "activity", "value": "activity"},
	{"label": "add-circle-empty", "value": "add-circle-empty"},
	{"label": "add-circle", "value": "add-circle"},
	{"label": "add", "value": "add"},
	{"label": "alert-triangle", "value": "alert-triangle"},
	{"label": "alert", "value": "alert"},
	{"label": "align-center", "value": "align-center"},
	{"label": "align-left", "value": "align-left"},
	{"label": "align-right", "value": "align-right"},
	{"label": "all-apps", "value": "all-apps"},
	{"label": "application-generic", "value": "application-generic"},
	{"label": "archive", "value": "archive"},
	{"label": "arrow-down-rounded", "value": "arrow-down-rounded"},
	{"label": "arrow-down-triangle", "value": "arrow-down-triangle"},
	{"label": "arrow-down", "value": "arrow-down"},
	{"label": "arrow-left-rounded", "value": "arrow-left-rounded"},
	{"label": "arrow-left", "value": "arrow-left"},
	{"label": "arrow-right-rounded", "value": "arrow-right-rounded"},
	{"label": "arrow-right", "value": "arrow-right"},
	{"label": "arrow-up-rounded", "value": "arrow-up-rounded"},
	{"label": "arrow-up", "value": "arrow-up"},
	{"label": "article-document", "value": "article-document"},
	{"label": "barcode", "value": "barcode"},
	{"label": "blog", "value": "blog"},
	{"label": "book-open", "value": "book-open"},
	{"label": "book", "value": "book"},
	{"label": "boolean", "value": "boolean"},
	{"label": "bot", "value": "bot"},
	{"label": "brand-mobile", "value": "brand-mobile"},
	{"label": "brand-now", "value": "brand-now"},
	{"label": "brand-service", "value": "brand-service"},
	{"label": "brand-servicenow", "value": "brand-servicenow"},
	{"label": "calendar", "value": "calendar"},
	{"label": "cards", "value": "cards"},
	{"label": "cart-full", "value": "cart-full"},
	{"label": "cart", "value": "cart"},
	{"label": "catalog", "value": "catalog"},
	{"label": "chart-do", "value": "chart-do"},
	{"label": "chart-pi", "value": "chart-pi"},
	{"label": "check-circle", "value": "check-circle"},
	{"label": "check", "value": "check"},
	{"label": "checkbox-checked", "value": "checkbox-checked"},
	{"label": "checkbox-empty", "value": "checkbox-empty"},
	{"label": "chevron-down", "value": "chevron-down"},
	{"label": "chevron-left", "value": "chevron-left"},
	{"label": "chevron-right-circle-solid", "value": "chevron-right-circle-solid"},
	{"label": "chevron-right-circle", "value": "chevron-right-cicle"},
	{"label": "chevron-right", "value": "chevron-right"},
	{"label": "chevron-up", "value": "chevron-up"},
	{"label": "circle-solid", "value": "circle-solid"},
	{"label": "clear-cache", "value": "clear-cache"},
	{"label": "clear", "value": "clear"},
	{"label": "clockwise", "value": "clockwise"},
	{"label": "code", "value": "code"},
	{"label": "cog-changes", "value": "cog-changes"},
	{"label": "cog-selected", "value": "cog-selected"},
	{"label": "cog", "value": "cog"},
	{"label": "collaboration", "value": "collaboration"},
	{"label": "comment-add", "value": "comment-add"},
	{"label": "comment-hollow", "value": "comment-hollow"},
	{"label": "comment", "value": "comment"},
	{"label": "company-feed", "value": "company-feed"},
	{"label": "condition", "value": "condition"},
	{"label": "configuration", "value": "configuration"},
	{"label": "connect-adduser-sm", "value": "connect-adduser-sm"},
	{"label": "connect-adduser", "value": "connect-adduser"},
	{"label": "connect-close-sm", "value": "connect-close-sm"},
	{"label": "connect-close", "value": "connect-close"},
	{"label": "connect-minimize-sm", "value": "connect-minimize-sm"},
	{"label": "connect-minimize", "value": "connect-minimize"},
	{"label": "connect-newwin", "value": "connect-newwin"},
	{"label": "connect-newwindow-sm", "value": "connect-newwindow-sm"},
	{"label": "connect-viewdocument-sm", "value": "connect-viewdocument-sm"},
	{"label": "connect-viewdocument", "value": "connect-viewdocument"},
	{"label": "connection", "value": "connection"},
	{"label": "console", "value": "console"},
	{"label": "copy", "value": "copy"},
	{"label": "counter-clockwise", "value": "counter-clockwise"},
	{"label": "cross-circle", "value": "cross-circle"},
	{"label": "cross", "value": "cross"},
	{"label": "cursor-move", "value": "cursor-move"},
	{"label": "cursor-select", "value": "cursor-select"},
	{"label": "dashboard", "value": "dashboard"},
	{"label": "database-error", "value": "database-error"},
	{"label": "database", "value": "database"},
	{"label": "date-time", "value": "date-time"},
	{"label": "debug", "value": "debug"},
	{"label": "default-knowledge-base", "value": "default-knowledge-base"},
	{"label": "delete-selected", "value": "delete-selected"},
	{"label": "delete", "value": "delete"},
	{"label": "directions", "value": "directions"},
	{"label": "discovery-connection", "value": "discovery-connection"},
	{"label": "discovery-identification", "value": "discovery-identification"},
	{"label": "discovery-pattern", "value": "discovery-pattern"},
	{"label": "discovery-square", "value": "discovery-square"},
	{"label": "discovery-step", "value": "discovery-step"},
	{"label": "document-all-generic", "value": "document-all-generic"},
	{"label": "document-attachment", "value": "document-attachment"},
	{"label": "document-code", "value": "document-code"},
	{"label": "document-doc", "value": "document-doc"},
	{"label": "document-multiple", "value": "document-multiple"},
	{"label": "document-pdf", "value": "document-pdf"},
	{"label": "document-ppt", "value": "document-ppt"},
	{"label": "document-txt", "value": "document-txt"},
	{"label": "document-xls", "value": "document-xls"},
	{"label": "document-zip", "value": "document-zip"},
	{"label": "document", "value": "document"},
	{"label": "double-chevron-left", "value": "double-chevron-left"},
	{"label": "double-chevron-right", "value": "double-chevron-right"},
	{"label": "download-sourcecode", "value": "download-sourcecode"},
	{"label": "download", "value": "download"},
	{"label": "drag-dots", "value": "drag-dots"},
	{"label": "drag", "value": "drag"},
	{"label": "drawer-selected", "value": "drawer-selected"},
	{"label": "drawer", "value": "drawer"},
	{"label": "edit-syntax", "value": "edit-syntax"},
	{"label": "edit", "value": "edit"},
	{"label": "ellipsis-vertical", "value": "ellipsis-vertical"},
	{"label": "ellipsis", "value": "ellipsis"},
	{"label": "empty-circle", "value": "empty-circle"},
	{"label": "endpoint", "value": "endpoint"},
	{"label": "envelope-open", "value": "envelope-open"},
	{"label": "envelope-subscribe", "value": "envelope-subscribe"},
	{"label": "envelope-unsubscribe", "value": "envelope-unsubscribe"},
	{"label": "error-circle", "value": "error-circle"},
	{"label": "error", "value": "error"},
	{"label": "essentials", "value": "essentials"},
	{"label": "export", "value": "export"},
	{"label": "filter", "value": "filter"},
	{"label": "first", "value": "first"},
	{"label": "fit-width", "value": "fit-width"},
	{"label": "floor-plan", "value": "floor-plan"},
	{"label": "folder", "value": "folder"},
	{"label": "form", "value": "form"},
	{"label": "format", "value": "format"},
	{"label": "fullscreen", "value": "fullscreen"},
	{"label": "glasses", "value": "glasses"},
	{"label": "global", "value": "global"},
	{"label": "hardware", "value": "hardware"},
	{"label": "help", "value": "help"},
	{"label": "history", "value": "history"},
	{"label": "home", "value": "home"},
	{"label": "hr", "value": "hr"},
	{"label": "identification", "value": "identification"},
	{"label": "image", "value": "image"},
	{"label": "indent", "value": "indent"},
	{"label": "info", "value": "info"},
	{"label": "insert-table", "value": "insert-table"},
	{"label": "it", "value": "it"},
	{"label": "key", "value": "key"},
	{"label": "keyboard", "value": "keyboard"},
	{"label": "label-dot", "value": "label-dot"},
	{"label": "label", "value": "label"},
	{"label": "last", "value": "last"},
	{"label": "layout", "value": "layout"},
	{"label": "lightbulb", "value": "lightbulb"},
	{"label": "like", "value": "like"},
	{"label": "link", "value": "link"},
	{"label": "list", "value": "list"},
	{"label": "livefeed", "value": "livefeed"},
	{"label": "loading", "value": "loading"},
	{"label": "location", "value": "location"},
	{"label": "locked", "value": "locked"},
	{"label": "loop", "value": "loop"},
	{"label": "mail", "value": "mail"},
	{"label": "marker", "value": "marker"},
	{"label": "maximize", "value": "maximize"},
	{"label": "menu-arrows", "value": "menu-arrows"},
	{"label": "menu", "value": "menu"},
	{"label": "minimize", "value": "minimize"},
	{"label": "mobile", "value": "mobile"},
	{"label": "move", "value": "move"},
	{"label": "my-feed", "value": "my-feed"},
	{"label": "navigator", "value": "navigator"},
	{"label": "new-above", "value": "new-above"},
	{"label": "new-below", "value": "new-below"},
	{"label": "new-ticket", "value": "new-ticket"},
	{"label": "new-window", "value": "new-window"},
	{"label": "not-started-circle", "value": "not-started-circle"},
	{"label": "notification-bell", "value": "notification-bell"},
	{"label": "number", "value": "number"},
	{"label": "open-document-new-tab", "value": "open-document-new-tab"},
	{"label": "or", "value": "or"},
	{"label": "panel-display-bottom", "value": "panel-display-bottom"},
	{"label": "panel-display-popout", "value": "panel-display-popout"},
	{"label": "panel-display-right", "value": "panel-display-right"},
	{"label": "paperclip", "value": "paperclip"},
	{"label": "pause", "value": "pause"},
	{"label": "percent", "value": "percent"},
	{"label": "phone", "value": "phone"},
	{"label": "phonecall-incoming", "value": "phonecall-incoming"},
	{"label": "phonecall-keypad", "value": "phonecall-keypad"},
	{"label": "phonecall-outgoing", "value": "phonecall-outgoing"},
	{"label": "play", "value": "play"},
	{"label": "poll", "value": "poll"},
	{"label": "pop-in", "value": "pop-in"},
	{"label": "pop-out", "value": "pop-out"},
	{"label": "power", "value": "power"},
	{"label": "preview", "value": "preview"},
	{"label": "print", "value": "print"},
	{"label": "queue", "value": "queue"},
	{"label": "radio-numeric-scale", "value": "radio-numeric-scale"},
	{"label": "radio-scale", "value": "radio-scale"},
	{"label": "redo-action", "value": "redo-action"},
	{"label": "refresh", "value": "refresh"},
	{"label": "remove", "value": "remove"},
	{"label": "replace-all", "value": "replace-all"},
	{"label": "replace", "value": "replace"},
	{"label": "required", "value": "required"},
	{"label": "run-command", "value": "run-command"},
	{"label": "save", "value": "save"},
	{"label": "script-check", "value": "script-check"},
	{"label": "script-comment", "value": "script-comment"},
	{"label": "script", "value": "script"},
	{"label": "search-database", "value": "search-database"},
	{"label": "search", "value": "search"},
	{"label": "select", "value": "select"},
	{"label": "server", "value": "server"},
	{"label": "share", "value": "share"},
	{"label": "software", "value": "software"},
	{"label": "sort-ascending", "value": "sort-ascending"},
	{"label": "sort-descending", "value": "sort-descending"},
	{"label": "sp-wishlist-sm", "value": "sp-wishlist-sm"},
	{"label": "sp-wishlist", "value": "sp-wishlist"},
	{"label": "spell-check", "value": "spell-check"},
	{"label": "split-vertical", "value": "split-vertical"},
	{"label": "star-empty", "value": "star-empty"},
	{"label": "star", "value": "star"},
	{"label": "step-in", "value": "step-in"},
	{"label": "step-out", "value": "step-out"},
	{"label": "step-over", "value": "step-over"},
	{"label": "stop-watch", "value": "stop-watch"},
	{"label": "stop", "value": "stop"},
	{"label": "stream-all-input", "value": "stream-all-input"},
	{"label": "stream-one-input", "value": "stream-one-input"},
	{"label": "string", "value": "string"},
	{"label": "sub-elements", "value": "sub-elements"},
	{"label": "subtract", "value": "subtract"},
	{"label": "success-circle", "value": "success-circle"},
	{"label": "success", "value": "success"},
	{"label": "syntax-check", "value": "syntax-check"},
	{"label": "tab", "value": "tab"},
	{"label": "table-sm", "value": "table-sm"},
	{"label": "table", "value": "table"},
	{"label": "tack", "value": "tack"},
	{"label": "target", "value": "target"},
	{"label": "template", "value": "template"},
	{"label": "text-bold", "value": "text-bold"},
	{"label": "text-italic", "value": "text-italic"},
	{"label": "text-style-add", "value": "text-style-add"},
	{"label": "text-style-clear", "value": "text-style-clear"},
	{"label": "text-underlined", "value": "text-underlined"},
	{"label": "text", "value": "text"},
	{"label": "threshold", "value": "threshold"},
	{"label": "timeline", "value": "timeline"},
	{"label": "today", "value": "today"},
	{"label": "translation", "value": "translation"},
	{"label": "trash", "value": "trash"},
	{"label": "tree-right", "value": "tree-right"},
	{"label": "tree", "value": "tree"},
	{"label": "undo-action", "value": "undo-action"},
	{"label": "undo", "value": "undo"},
	{"label": "unindent", "value": "unindent"},
	{"label": "unlink", "value": "unlink"},
	{"label": "unlocked", "value": "unlocked"},
	{"label": "upload", "value": "upload"},
	{"label": "user-add", "value": "user-add"},
	{"label": "user-group", "value": "user-group"},
	{"label": "user-profile", "value": "user-profile"},
	{"label": "user-selected", "value": "user-selected"},
	{"label": "user-subtract", "value": "user-subtract"},
	{"label": "user", "value": "user"},
	{"label": "vcr-down", "value": "vcr-down"},
	{"label": "vcr-left", "value": "vcr-left"},
	{"label": "vcr-right", "value": "vcr-right"},
	{"label": "vcr-up", "value": "vcr-up"},
	{"label": "video", "value": "video"},
	{"label": "view", "value": "view"},
	{"label": "vtb-flexible-outline", "value": "vtb-flexible-outline"},
	{"label": "vtb-flexible-sm", "value": "vtb-flexible-sm"},
	{"label": "vtb-flexible", "value": "vtb-flexible"},
	{"label": "vtb-freeform-sm", "value": "vtb-freeform-sm"},
	{"label": "vtb-freeform", "value": "vtb-freeform"},
	{"label": "vtb-guided-sm", "value": "vtb-guided-sm"},
	{"label": "vtb-guided", "value": "vtb-guided"},
	{"label": "warning-circle", "value": "warning-circle"},
	{"label": "wishlist-sm", "value": "wishlist-sm"},
	{"label": "wishlist", "value": "wishlist"},
	{"label": "work-note", "value": "work-note"},
	{"label": "workflow-active", "value": "workflow-active"},
	{"label": "workflow-approval-action", "value": "workflow-approval-action"},
	{"label": "workflow-approval-rejected", "value": "workflow-approval-rejected"},
	{"label": "workflow-approved", "value": "workflow-approved"},
	{"label": "workflow-check", "value": "workflow-check"},
	{"label": "workflow-complete", "value": "workflow-complete"},
	{"label": "workflow-late", "value": "workflow-late"},
	{"label": "workflow-on-hold", "value": "workflow-on-hold"},
	{"label": "workflow-pending", "value": "workflow-pending"},
	{"label": "workflow-progress", "value": "workflow-progress"},
	{"label": "workflow-rejected", "value": "workflow-rejected"},
	{"label": "workflow-requested", "value": "workflow-requested"},
	{"label": "workflow-skip", "value": "workflow-skip"},
	{"label": "workflow", "value": "workflow"},
	{"label": "zoom-in", "value": "zoom-in"},
	{"label": "zoom-out", "value": "zoom-out"}
];

Once I had my array, it was just a matter of throwing a little HTML together to build the SELECT statement and to loop through the array to create all of the OPTION statements.

<div>
  <select ng-model="data.selected" size="10">
    <option ng-repeat="opt in data.option" class="icon icon-{{opt.value}}" value="{{opt.value}}">   {{opt.label}}</option>
  </select>
</div>

That was enough to display the results, so I built myself a little test page to launch the widget in a modal popup, just to see how it looked.

Modal pop-up of icon SELECT statement

Well, I’m not sure how that blank option got in there in that first position, but other than that, it looks pretty good. Now I just have to figure out how to get the selection back to the main widget once the operator makes their choice. Functionally, there are a couple of different ways to go here: you could have user click on their selection and then click on an OK button to complete the process, or you could go with the assumption that clicking on an option was final and just close out the window right then and there and pass the choice back to the primary screen. Personally, I lean towards the second option; it’s a little rude, but it saves you an unnecessary extra click. From my perspective, when you make a selection, you’re done and we’re out of here.

The first thing to do then, is to add an ng-change to the SELECT statement.

<div>
  <select ng-model="data.selected" ng-change="iconSelected();" size="10">
    <option ng-repeat="opt in data.option" class="icon icon-{{opt.value}}" value="{{opt.value}}">   {{opt.label}}</option>
  </select>
</div>

Then we have to create the referenced function in the Client controller. The behavior that we want is for the pop-up to return the selection to the caller and then go away. I wasn’t exactly sure how to do that, but I did a little research and it turns out that there are a couple of options here as well. You can send in a shared object as one of the widget options when you first open the spModal window, which gives you a place to store things that both the opener and the opened can access, or you can emulate a button click, and pass information back as an argument to the click function. The syntax on that last one seems a little bizarre, but I tried it and it worked, so I decided to go that route. My function turned out to simply be this:

$scope.iconSelected = function() {
	$scope.$parent.$parent.buttonClicked({selected: c.data.selected});
};

Passing the value of the selected option in the buttonClicked function both returns the data to the caller and closes the modal window. Sweet! To open the window and grab the selected value, the code in my little test page ended up looking like this:

spModal.open({
	title: 'Select Icon',
	widget: 'icon-picker',
	buttons: [
		{label: '${Cancel}', cancel: true}
	],
	size: 'sm',
}).then(function(response) {
	alert(JSON.stringify(response));
}, function () {
	alert('Cancelled');
});

You will notice that there are two response functions, one for a normal completion of the task and a second one to handle the Cancel button or the forced closing of the modal pop-up. Using this temporary test page, we can launch the modal and click around and see what happens. If we select an option, we should get an alert that contains the option value selected.

Alert showing the option selected

Well, that all seems to work. In my little test page, all I do is pop the alert to prove that things are working. In actual use, I would update the value of the target field, but at this point, I’m just focused on building the pop-up selector. Building out a function that will actually use this pop-up selector is a project for another day.