-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmenu.go
155 lines (131 loc) · 4.97 KB
/
menu.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Package menu provides a data structure to represent nested menus such as
// those that might appear on a web page.
//
// The basic unit is a ListItem, which may be either a List or an Item.
// An Item consists of a label (e.g. the text that appears, or perhaps a key
// to a translation table when localized text is needed) and an action (e.g.
// a url path). A List consists of a label and a list of ListItems.
//
// An Item may optionally have a visibility mask, which may be used when
// certain parts of the menu need to be dynamically hidden. A example of
// when this might be useful is an admin submenu which is only visible to
// authenticated admin users.
//
// The Filtered() method is used to hide parts of a menu based on a bitmask.
// The provided bitmask is and-ed with the visibility mask of each Item, and
// the item is shown only if the result is nonzero.
//
// A visibility mask may also be applied to a List. If a List is hidden,
// all the Lists and Items under it are also hidden.
//
// Menus hay have arbitrary depth, although recursive menus are not supported
// and will crash.
//
// Menus play well with json.Marshal (see the examples).
package menu
import (
"strings"
)
// A ListItem represents either an List or an Item.
type ListItem interface {
// Display text for this item. If i18n support is required, put the
// translation key here.
Label() string
// Action to take if this item is selected, e.g. a destination path.
// If the case of a List, the action should be blank.
Action() string
// True if this ListItem is a List.
IsList() bool
// For ListItems with a visibility mask, tell us if this ListItem is
// visible for the given bitmask. For Items without a visibility mask,
// this method always returns true. For Lists, if all the descendants
// are not visible, then the List is also not visible.
//
// Note that empty Lists are always considered not visible. This has
// the consequence that a filtered List will never contain ListItems
// that are empty Lists.
IsVisible(int64) bool
// If this is a List, a slice containing all its items. Returns nil for
// Items.
Items() []ListItem
// If this is a List, return a copy of itself with all the invisible
// items filtered out. If this is an Item, return itself.
Filtered(int64) ListItem
// Return a string representation with the specified number of tabs prepended
// to the beginning of every line. Note that the string representation of a
// List is multiline.
IndentedString(int) string
// Return a string representation. This is equivalent to IndentedString(0).
String() string
}
// Type item implements ListItem for single Items.
type item struct {
Lab string `json:"label"`
Act string `json:"action"`
visibility int64
}
func (i *item) Label() string { return i.Lab }
func (i *item) Action() string { return i.Act }
func (i *item) IsList() bool { return false }
func (i *item) Items() []ListItem { return nil }
func (i *item) IsVisible(m int64) bool { return m&i.visibility != 0 }
func (i *item) Filtered(m int64) ListItem { return i }
func (i *item) IndentedString(n int) string {
return strings.Repeat("\t", n) + i.String()
}
func (i *item) String() string { return i.Lab + ": " + i.Act }
// Type list implements ListItem for Lists.
type list struct {
Lab string `json:"label"`
Ims []ListItem `json:"items"`
visibility int64
}
func (l *list) Label() string { return l.Lab }
func (l *list) Action() string { return "" }
func (l *list) IsList() bool { return true }
func (l *list) Items() []ListItem { return l.Ims }
func (l *list) IsVisible(m int64) bool {
if m&l.visibility == 0 {
return false
}
for _, item := range l.Ims {
if item.IsVisible(m) {
return true
}
}
return false
}
func (l *list) Filtered(m int64) ListItem {
filtered := []ListItem{}
for _, item := range l.Ims {
if item.IsVisible(m) {
filtered = append(filtered, item.Filtered(m))
}
}
return &list{Lab: l.Lab, Ims: filtered}
}
func (l *list) IndentedString(n int) string {
x := []string{strings.Repeat("\t", n) + l.Lab}
for _, item := range l.Ims {
x = append(x, item.IndentedString(n+1))
}
return strings.Join(x, "\n")
}
func (l *list) String() string { return l.IndentedString(0) }
// NewMenu returns a new List containing the given items.
func NewList(label string, items []ListItem) ListItem {
return &list{Lab: label, Ims: items, visibility: -1}
}
// NewListMask returns a new List containing the given items and with the
// given visiblity mask.
func NewListMask(label string, items []ListItem, visibility int64) ListItem {
return &list{Lab: label, Ims: items, visibility: visibility}
}
// NewItem returns a new Item.
func NewItem(label, action string) ListItem {
return &item{Lab: label, Act: action, visibility: -1}
}
// NewItemMask returns a new Item with the given visibility mask.
func NewItemMask(label, action string, visibility int64) ListItem {
return &item{Lab: label, Act: action, visibility: visibility}
}