2022-09-22 15:04:48 -05:00
package navtree
2022-09-28 01:29:35 -05:00
import (
"encoding/json"
"sort"
)
2022-09-22 15:04:48 -05:00
const (
// These weights may be used by an extension to reliably place
// itself in relation to a particular item in the menu. The weights
// are negative to ensure that the default items are placed above
// any items with default weight.
2022-10-03 09:05:19 -05:00
WeightHome = ( iota - 20 ) * 100
WeightSavedItems
2022-09-22 15:04:48 -05:00
WeightCreate
WeightDashboard
2022-10-07 13:31:45 -05:00
WeightQueryLibrary
2022-09-22 15:04:48 -05:00
WeightExplore
WeightAlerting
WeightDataConnections
WeightPlugin
WeightConfig
2022-10-05 04:46:27 -05:00
WeightAlertsAndIncidents
WeightMonitoring
WeightApps
2022-09-22 15:04:48 -05:00
WeightAdmin
WeightProfile
WeightHelp
)
const (
NavSectionCore string = "core"
NavSectionPlugin string = "plugin"
NavSectionConfig string = "config"
)
2022-09-28 01:29:35 -05:00
const (
2022-11-18 03:05:45 -06:00
NavIDRoot = "root"
2022-09-28 01:29:35 -05:00
NavIDDashboards = "dashboards"
NavIDDashboardsBrowse = "dashboards/browse"
NavIDCfg = "cfg" // NavIDCfg is the id for org configuration navigation node
NavIDAdmin = "admin"
NavIDAlertsAndIncidents = "alerts-and-incidents"
NavIDAlerting = "alerting"
NavIDMonitoring = "monitoring"
NavIDReporting = "reports"
2022-10-05 04:46:27 -05:00
NavIDApps = "apps"
2022-09-28 01:29:35 -05:00
)
2022-09-22 15:04:48 -05:00
type NavLink struct {
Id string ` json:"id,omitempty" `
Text string ` json:"text" `
Section string ` json:"section,omitempty" `
SubTitle string ` json:"subTitle,omitempty" `
Icon string ` json:"icon,omitempty" ` // Available icons can be browsed in Storybook: https://developers.grafana.com/ui/latest/index.html?path=/story/docs-overview-icon--icons-overview
Img string ` json:"img,omitempty" `
Url string ` json:"url,omitempty" `
Target string ` json:"target,omitempty" `
SortWeight int64 ` json:"sortWeight,omitempty" `
Divider bool ` json:"divider,omitempty" `
HideFromMenu bool ` json:"hideFromMenu,omitempty" `
HideFromTabs bool ` json:"hideFromTabs,omitempty" `
ShowIconInNavbar bool ` json:"showIconInNavbar,omitempty" `
RoundIcon bool ` json:"roundIcon,omitempty" `
2022-10-31 05:01:34 -05:00
IsSection bool ` json:"isSection,omitempty" `
2022-09-22 15:04:48 -05:00
Children [ ] * NavLink ` json:"children,omitempty" `
HighlightText string ` json:"highlightText,omitempty" `
HighlightID string ` json:"highlightId,omitempty" `
EmptyMessageId string ` json:"emptyMessageId,omitempty" `
2022-11-03 15:19:42 -05:00
PluginID string ` json:"pluginId,omitempty" ` // (Optional) The ID of the plugin that registered nav link (e.g. as a standalone plugin page)
2022-11-15 06:08:15 -06:00
IsCreateAction bool ` json:"isCreateAction,omitempty" `
2022-09-22 15:04:48 -05:00
}
2022-09-28 01:29:35 -05:00
func ( node * NavLink ) Sort ( ) {
Sort ( node . Children )
}
type NavTreeRoot struct {
Children [ ] * NavLink
}
func ( root * NavTreeRoot ) AddSection ( node * NavLink ) {
root . Children = append ( root . Children , node )
}
func ( root * NavTreeRoot ) RemoveSection ( node * NavLink ) {
var result [ ] * NavLink
for _ , child := range root . Children {
if child != node {
result = append ( result , child )
}
}
root . Children = result
}
func ( root * NavTreeRoot ) FindById ( id string ) * NavLink {
return FindById ( root . Children , id )
}
func ( root * NavTreeRoot ) RemoveEmptySectionsAndApplyNewInformationArchitecture ( topNavEnabled bool ) {
// Remove server admin node if it has no children or set the url to first child
if node := root . FindById ( NavIDAdmin ) ; node != nil {
if len ( node . Children ) == 0 {
root . RemoveSection ( node )
} else {
node . Url = node . Children [ 0 ] . Url
}
}
if topNavEnabled {
orgAdminNode := root . FindById ( NavIDCfg )
if orgAdminNode != nil {
orgAdminNode . Url = "/admin"
orgAdminNode . Text = "Administration"
}
if serverAdminNode := root . FindById ( NavIDAdmin ) ; serverAdminNode != nil {
2022-10-12 04:01:33 -05:00
serverAdminNode . Url = "/admin/server"
2022-10-05 04:46:27 -05:00
serverAdminNode . SortWeight = 0
2022-09-28 01:29:35 -05:00
if orgAdminNode != nil {
orgAdminNode . Children = append ( orgAdminNode . Children , serverAdminNode )
root . RemoveSection ( serverAdminNode )
}
}
// Move reports into dashboards
if reports := root . FindById ( NavIDReporting ) ; reports != nil {
if dashboards := root . FindById ( NavIDDashboards ) ; dashboards != nil {
reports . SortWeight = 0
dashboards . Children = append ( dashboards . Children , reports )
root . RemoveSection ( reports )
}
}
// Change id of dashboards
if dashboards := root . FindById ( NavIDDashboards ) ; dashboards != nil {
dashboards . Id = "dashboards/browse"
}
}
// Remove top level cfg / administration node if it has no children (needs to be after topnav new info archicture logic above that moves server admin into it)
// Remove server admin node if it has no children or set the url to first child
if node := root . FindById ( NavIDCfg ) ; node != nil {
if len ( node . Children ) == 0 {
root . RemoveSection ( node )
} else if ! topNavEnabled {
node . Url = node . Children [ 0 ] . Url
}
}
}
func ( root * NavTreeRoot ) Sort ( ) {
Sort ( root . Children )
}
func ( root * NavTreeRoot ) MarshalJSON ( ) ( [ ] byte , error ) {
return json . Marshal ( root . Children )
}
func Sort ( nodes [ ] * NavLink ) {
sort . SliceStable ( nodes , func ( i , j int ) bool {
iw := nodes [ i ] . SortWeight
if iw == 0 {
iw = int64 ( i ) + 1
}
jw := nodes [ j ] . SortWeight
if jw == 0 {
jw = int64 ( j ) + 1
}
2022-09-22 15:04:48 -05:00
2022-09-28 01:29:35 -05:00
return iw < jw
} )
for _ , child := range nodes {
child . Sort ( )
2022-09-22 15:04:48 -05:00
}
2022-09-28 01:29:35 -05:00
}
func FindById ( nodes [ ] * NavLink , id string ) * NavLink {
for _ , child := range nodes {
if child . Id == id {
return child
} else if len ( child . Children ) > 0 {
if found := FindById ( child . Children , id ) ; found != nil {
return found
}
}
2022-09-22 15:04:48 -05:00
}
2022-09-28 01:29:35 -05:00
return nil
2022-09-22 15:04:48 -05:00
}