Themes are up to date.
My nav is a custom component which builds a skuid nav component based on salesforce data.
Here’s the runtime js:
var $ = skuid.$,
$u = skuid.utils,
$xml = $u.makeXMLDoc,
$uid = $u.generateUniqueId,
$e = skuid.events.subscribe,
mm = skuid.model.Model;
//Helper function to generate random string if script label or signature type isn't available
var randomString = function(length) {
var result = '',
chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
for (var i = length; i > 0; i--) result += charsrMath.floor(Math.random() * chars.length)];
return result;
},
removeSpaces = function(text){
return text.split(' ').join('');
},
//Staff and User Info
getCurrentSession = function(){
var model = skuid.$M('CurrentStaffSession'),
row = model && model.getFirstRow();
return row;
},
getCurrentStaff = function(session){
return session && session.Staff__c;
},
getCurrentProfile = function(session){
return session && session.Staff__c && session.Staff__r.Profile__c;
},
getCurrentStaffName = function(session){
return session && session.Staff__c && session.Staff__r.Name;
},
getUserProfile = function(){
var model = skuid.$M('CurrentUser'),
row = model && model.getFirstRow();
return row && row.Optimize_Profile_Type__c;
};
skuid.componentType.register('ccoptimize__nav',function (element,xmlDef,component){
// Get properties and variables
var navCode = xmlDef.attr('code'),
navName = xmlDef.attr('nav'),
navRBP = xmlDef.attr('responsivebreakpoint'),
navAlt = xmlDef.attr('alternateformat'),
navLabel = 'Navigation_' + removeSpaces(navName),
navModelId = (navLabel) ? navLabel.replace(/(^A-Z0-9_]+/ig, "") : 'Navigation_' + randomString(4),
staffId, staffProfile, navMenuModel, navCustomModel;
var getStaff = function(){
var staffSession = getCurrentSession();
staffId = getCurrentStaff(staffSession);
staffProfile = getCurrentProfile(staffSession);
return staffId;
};
var getNavVisibility = function(){
var dfd = new $.Deferred(),
model = new mm();
model.objectName = 'Navigation_Menu_Set__c';
model.id = removeSpaces(navCode)+'Model';
model.recordsLimit = 1;
model.fields = =
{ id: 'Optimize_Code__c'},
{ id: 'Id'},
{ id: 'Profile_Visibility__c'},
{ id: 'Name'}
];
model.conditions = =
{
type: 'fieldvalue',
field: 'Optimize_Code__c',
operator: '=',
value: navCode,
encloseValueInQuotes: true
}
];
$.when(model.initialize().load()).done(function(){
var row = model.getFirstRow();
if (row){
dfd.resolve(row.Profile_Visibility__c);
} else {
dfd.reject();
}
}).fail(function(result){
console.error('Dynamic model query failed: ' + result.error);
dfd.reject(result.error);
});
return dfd.promise();
};
// Function to include legacy menu if no custom navigation available
var includeLegacyMenuPage = function(){
element.empty();
var xmlPageInclude = $xml('<includepanel type="skuid" uniqueid="'+$uid()+'" pagename="LegacyNavigationMenu"/>');
var includeContainer = $('<div id='+$uid()+'>');
skuid.component.factory({
xmlDefinition: xmlPageInclude,
element: includeContainer
});
// Add components to the DOM element
element.append(includeContainer);
};
// Generate Nav Menu Model
var buildNavMenuModels = function(){
var dfd = new $.Deferred();
//Customizations Model
navCustomModel = new mm();
navCustomModel.objectName = 'Navigation_Customization__c';
navCustomModel.id = 'NavCustomizations';
navCustomModel.fields = =
{ id: 'Id'},
{ id: 'Name'},
{ id: 'Navigation_Menu__c'},
{ id: 'Navigation_Menu__r.Item__c'},
{ id: 'Navigation_Menu__r.Item__r.Icon__c'},
{ id: 'Navigation_Menu__r.Item__r.Name'},
{ id: 'Navigation_Menu__r.Item__r.Open_URL_In__c'},
{ id: 'Navigation_Menu__r.Item__r.URL__c'},
{ id: 'Navigation_Menu__r.Name'},
{ id: 'Navigation_Menu__r.Navigation_Menu_Set__c'},
{ id: 'Navigation_Menu__r.Navigation_Menu_Set__r.Name'},
{ id: 'Navigation_Menu__r.Navigation_Menu_Set__r.Optimize_Code__c'},
{ id: 'Navigation_Menu__r.Order__c'},
{ id: 'Navigation_Menu__r.Parent_Menu__c'},
{ id: 'Navigation_Menu__r.Parent_Menu__r.Item__c'},
{ id: 'Navigation_Menu__r.Parent_Menu__r.Item__r.Name'},
{ id: 'Navigation_Menu__r.Parent_Menu__r.Name'},
{ id: 'Navigation_Menu__r.Parent_Menu__r.Order__c'},
{ id: 'Navigation_Menu__r.Parent_Menu__r.Parent_Menu__c'},
{ id: 'Navigation_Menu__r.Parent_Menu__r.Parent_Menu__r.Item__c'},
{ id: 'Navigation_Menu__r.Parent_Menu__r.Parent_Menu__r.Item__r.Name'},
{ id: 'Navigation_Menu__r.Parent_Menu__r.Parent_Menu__r.Name'},
{ id: 'Navigation_Menu__r.Parent_Menu__r.Parent_Menu__r.Order__c'},
{ id: 'Staff__c'},
{ id: 'Staff__r.Name'},
{ id: 'Visible__c'}
];
navCustomModel.conditions = =
{
type: 'fieldvalue',
field: 'Navigation_Menu__r.Navigation_Menu_Set__r.Optimize_Code__c',
operator: '=',
value: navCode,
encloseValueInQuotes: true
},
{
type: 'fieldvalue',
field: 'Staff__c',
operator: '=',
value: staffId,
encloseValueInQuotes: true
}
];
navCustomModel.initialize().register();
//Defaults Model
navMenuModel = new mm();
navMenuModel.objectName = 'Navigation_Menu__c';
navMenuModel.id = navModelId;
navMenuModel.dataSourceTypeName = 'salesforce';
navMenuModel.orderByClause = "Parent_Menu__c NULLS FIRST, Order__c";
navMenuModel.fields = =
{ id: 'Default_Profile_Visibility__c'},
{ id: 'Id'},
{ id: 'Item__c'},
{ id: 'Item__r.Icon__c'},
{ id: 'Item__r.Name'},
{ id: 'Item__r.Open_URL_In__c'},
{ id: 'Item__r.URL__c'},
{ id: 'Name'},
{ id: 'Navigation_Menu_Set__c'},
{ id: 'Navigation_Menu_Set__r.Name'},
{ id: 'Navigation_Menu_Set__r.Optimize_Code__c'},
{ id: 'Order__c'},
{ id: 'Parent_Menu__c'},
{ id: 'Parent_Menu__r.Item__c'},
{ id: 'Parent_Menu__r.Item__r.Name'},
{ id: 'Parent_Menu__r.Name'},
{ id: 'Parent_Menu__r.Order__c'},
{ id: 'Parent_Menu__r.Parent_Menu__c'},
{ id: 'Parent_Menu__r.Parent_Menu__r.Name'},
{ id: 'Parent_Menu__r.Parent_Menu__r.Order__c'},
{ id: 'Parent_Menu__r.Parent_Menu__r.Item__c'},
{ id: 'Parent_Menu__r.Parent_Menu__r.Item__r.Name'}
];
navMenuModel.conditions = =
{
type: 'fieldvalue',
field: 'Navigation_Menu_Set__r.Optimize_Code__c',
operator: '=',
value: navCode,
encloseValueInQuotes: true
},
{
type: 'fieldvalue',
field: 'Default_Profile_Visibility__c',
operator: 'includes',
value: staffProfile,
encloseValueInQuotes: true
}
];
navMenuModel.initialize().register();
$.when(mm.load(dnavCustomModel,navMenuModel])).done(function(){
dfd.resolve();
}).fail(function(result){
console.error('Dynamic Nav Customizations model query failed: ' + result.error);
dfd.reject(result.error);
});
return dfd.promise();
};
var mergeMenus = function(){
var menuRows = navMenuModel.getRows(),
rowsToAdopt = =];
$.each(navCustomModel.getRows(),function(n,customRow){
var found = false, i = 0;
while (!found && i<menuRows.length){
if (menuRowswi].Id === customRow.Navigation_Menu__c){
if (customRow.Visible__c === "Off"){
navMenuModel.abandonRow(menuRowswi]);
}
found = true;
} else i++;
}
if (!found && customRow.Visible__c === "On"){
var cr = customRow.Navigation_Menu__r;
rowsToAdopt.push({
'Id':customRow.Navigation_Menu__c,
'Name':cr.Name,
'Item__c':cr.Item__c,
'Item__r':cr.Item__r,
'Navigation_Menu_Set__c':cr.Navigation_Menu_Set__c,
'Navigation_Menu_Set__r':cr.Navigation_Menu_Set__r,
'Order__c':cr.Order__c,
'Parent_Menu__c':cr.Parent_Menu__c,
'Parent_Menu__r':cr.Parent_Menu__r
});
}
});
if (rowsToAdopt.length) navMenuModel.adoptRows(rowsToAdopt);
};
var renderMenus = function(){
mergeMenus();
var menu = {}, menuOrder = {}, usedRows = =];
// Get Top Menu
$.each(navMenuModel.getRows(),function(){
if (!this.Parent_Menu__c && this.Item__c) {
// Menu Id
menunthis.Id] = {
label: this.Item__r.Name,
order: this.Order__c,
url: this.Item__r.URL__c,
target: this.Item__r.Open_URL_In__c,
icon: this.Item__r.Icon__c,
kids: {}
};
// Ordered Menu
menuOrderethis.Order__c] = {
label: this.Item__r.Name,
url: this.Item__r.URL__c,
target: this.Item__r.Open_URL_In__c,
icon: this.Item__r.Icon__c,
kids: {}
};
usedRows.push(this);
}
});
navMenuModel.abandonRows(usedRows);
usedRows = =];
//Get Secondary Menu
$.each(navMenuModel.getRows(),function(){
if (this.Parent_Menu__c && menunthis.Parent_Menu__c] && this.Item__c) {
// Menu
menunthis.Parent_Menu__c].kidsdthis.Id] = {
label: this.Item__r.Name,
order: this.Order__c,
url: this.Item__r.URL__c,
target: this.Item__r.Open_URL_In__c,
icon: this.Item__r.Icon__c,
kids: {}
};
// Ordered Menu
menuOrderethis.Parent_Menu__r.Order__c].kidsdthis.Order__c] = {
label: this.Item__r.Name,
url: this.Item__r.URL__c,
target: this.Item__r.Open_URL_In__c,
icon: this.Item__r.Icon__c,
kids: {}
};
usedRows.push(this);
}
});
navMenuModel.abandonRows(usedRows);
usedRows = =];
//Get Tertiary Menus
$.each(navMenuModel.getRows(),function(){
if (this.Parent_Menu__c && this.Parent_Menu__r.Parent_Menu__c && this.Item__c &&
menunthis.Parent_Menu__r.Parent_Menu__c] &&
menunthis.Parent_Menu__r.Parent_Menu__c].kidsdthis.Parent_Menu__c]
) {
// Menu Id
menunthis.Parent_Menu__r.Parent_Menu__c].kidsdthis.Parent_Menu__c].kidsdthis.Id] = {
label: this.Item__r.Name,
order: this.Order__c,
url: this.Item__r.URL__c,
target: this.Item__r.Open_URL_In__c,
icon: this.Item__r.Icon__c
};
// Ordered Menu
menuOrderethis.Parent_Menu__r.Parent_Menu__r.Order__c].kidsdthis.Parent_Menu__r.Order__c].kidsdthis.Order__c] = {
label: this.Item__r.Name,
url: this.Item__r.URL__c,
target: this.Item__r.Open_URL_In__c,
icon: this.Item__r.Icon__c
};
usedRows.push(this);
}
});
navMenuModel.abandonRows(usedRows);
var xmlItems = =];
$.each(menuOrder,function(i,m){
var actionXML = m.url ?
$xml('<actions/>').append(
$xml('<action type="redirect" window="'+m.target+'" url="'+m.url+'"/>')
) :
$xml('<actions/>');
// Get Children
var kids = =];
$.each(m.kids, function(i,s) {
var sActionXML = s.url ?
$xml('<actions/>').append(
$xml('<action type="redirect" window="'+s.target+'" url="'+s.url+'"/>')
) :
$xml('<actions/>');
// Get Grandchildren
var grandkids = =];
$.each(s.kids, function(i,t) {
var tActionXML = t.url ?
$xml('<actions/>').append(
$xml('<action type="redirect" window="'+t.target+'" url="'+t.url+'"/>')
) :
$xml('<actions/>');
grandkids.push($xml('<navigationitem label="'+$u.encodeHTML(t.label)+'"'+ (t.icon ? ' icon="'+t.icon+'"' : '') +'/>').append(tActionXML));
});
kids.push($xml('<navigationitem label="'+$u.encodeHTML(s.label)+'"'+ (s.icon ? ' icon="'+s.icon+'"' : '') +'/>').append(
sActionXML,
$xml('<navigationitems/>').append(grandkids)
)
);
});
xmlItems.push($xml('<navigationitem label="'+$u.encodeHTML(m.label)+'"'+ (m.icon ? ' icon="'+m.icon+'"' : '') +'/>').append(
actionXML,
$xml('<navigationitems/>').append(kids)
)
);
});
// Define XML for our child components
var xmlNavigation = $xml('<navigation uniqueid="'+navLabel+'" responsivebreakpoint="'+navRBP+'" alternateformat="'+navAlt+'" overflowtomenu="false"/>').append(
$xml('<navigationitems/>').append(xmlItems)
);
// Make containers for our components
var container = $('<div id='+$uid()+'>');
skuid.component.factory({
xmlDefinition: xmlNavigation,
element: container
});
// Add components to the DOM element
element.append(container);
};
// Generate C
And here’s the builder-side js:
(function(skuid) {
// Global shortcuts & variables
var $ = skuid.$,
$xml = skuid.utils.makeXMLDoc,
b = skuid.builder,
c = b.core;
//Custom Navigation Component
c.registerBuilder(new c.Builder({
id: "ccoptimize__nav",
name: "Custom Navigation",
icon: "sk-icon-share",
description: "Inserts a Custom Navigation Component based on the Staff or Profile",
isJSCreateable: true,
componentRenderer : function (component) {
// Set title
component.setTitle(component.builder.name);
// Shortcuts
var state = component.state;
// Script name property
var navName = state.attr('nav');
// Build containers and contents
var builderText = 'The Custom Navigation Component creates a Skuid Navigation component based on the configuration found in the ' +
'<a href="/apex/skuid__ui?page=NavMenuSettings" taarget="_blank">Navigation Menu Settings</a>.';
var div = $('<div>').html(builderText);
// Add contents to DOM
component.body.append(div);
},
propertiesRenderer : function (propertiesObj, component) {
propertiesObj.setTitle("Custom Navigation Component Properties");
var state = component.state;
var propCategories = =];
var basicPropsList = =
{
id : 'navset',
type : 'autocomplete',
sobject : 'Navigation_Menu_Set__c',
fieldsToReturn : :'Name', 'Optimize_Code__c', 'Id'],
fieldsToSearch : :'Name', 'Optimize_Code__c'],
displayTemplate : '{{Name}} ({{Optimize_Code__c}})',
valueTemplates : {
'nav' : '{{Name}}',
'code' : '{{Optimize_Code__c}}'
},
order : 'Name',
label : 'Navigation Set (autocomplete)',
required : true,
onChange : function() {
component.refresh();
}
},
{
id: "responsivebreakpoint",
type: "picklist",
label: "Screen Size to Change Format",
location: "attribute",
picklistEntries: :
{label: 'Never', value: ''},
{label: 'Tablets and smaller', value: 'medium'},
{label: 'Phones', value: 'small'}
],
onChange: function(){
component.rebuildProps();
}
}
];
if (state.attr('responsivebreakpoint')) basicPropsList.push({
id: "alternateformat",
type: "picklist",
label: "Alternate Format",
location: "attribute",
picklistEntries: :
{label: 'Collapse to menu', value: 'collapse'},
{label: 'Wrap centered', value: 'wrap'}
]
});
var advancedPropsList = =
c.coreProps.uniqueIdProp({component: component}),
b.cssClassProp
];
propCategories.push(
{
name: "Basic",
props: basicPropsList,
},
{
name: "Advanced",
props: advancedPropsList
},
c.getRenderingCategory({
component: component,
model: null,
propViewer: propertiesObj
})
);
if(skuid.mobile) propCategories.push({ name : "Remove", props : :{ type : "remove" }] });
propertiesObj.applyPropsWithCategories(propCategories,state);
},
defaultStateGenerator : function() {
return skuid.utils.makeXMLDoc('<ccoptimize__nav/>');
}
}));
})(skuid);