1 // Package tabs implements a simple tab interface for HTML documents 2 package tabs // import "vimagination.zapto.org/gopherjs/tabs" 3 4 import ( 5 "honnef.co/go/js/dom" 6 "vimagination.zapto.org/gopherjs/style" 7 "vimagination.zapto.org/gopherjs/xdom" 8 "vimagination.zapto.org/gopherjs/xjs" 9 ) 10 11 const css = `.tabs { 12 line-height : 24px; 13 position : relative; 14 width : 100%; 15 overflow : hidden; 16 margin : 0; 17 padding : 0 0 0 20px; 18 z-index : 0; 19 } 20 21 .tabs:after { 22 position : absolute; 23 content : ""; 24 width : 100%; 25 bottom : 0; 26 left : 0; 27 border-bottom: 1px solid #000; 28 z-index : 1; 29 overflow : hidden; 30 text-align : center; 31 transform : translateX(-20px); 32 } 33 34 .tabs > div { 35 border : 1px solid #000; 36 display : inline-block; 37 position : relative; 38 z-index : 1; 39 margin : 0 -5px; 40 padding : 0 20px; 41 border-top-right-radius: 6px; 42 border-top-left-radius: 6px; 43 background : linear-gradient(to bottom, #ececec 50%, #d1d1d1 100%); 44 box-shadow : 0 3px 3px rgba(0, 0, 0, 0.4), inset 0 1px 0 #fff; 45 text-shadow : 0 1px #fff; 46 user-select : none; 47 -moz-user-select : none; 48 -webkit-user-select : none; 49 } 50 51 .tabs > div:hover { 52 background : linear-gradient(to bottom, #faa 1%, #ffecec 50%, #d1d1d1 100%); 53 cursor : pointer; 54 } 55 56 .tabs > div:before, .tabs > div:after { 57 position : absolute; 58 bottom : -1px; 59 width : 6px; 60 height : 6px; 61 content : " "; 62 border : 1px solid #000; 63 } 64 65 .tabs > div:before { 66 left : -7px; 67 border-bottom-right-radius : 6px; 68 border-width : 0 1px 1px 0; 69 box-shadow: 2px 2px 0 #d1d1d1; 70 } 71 72 .tabs > div:after { 73 right : -7px; 74 border-bottom-left-radius : 6px; 75 border-width : 0 0 1px 1px; 76 box-shadow: -2px 2px 0 #d1d1d1; 77 } 78 79 .tabs > div.selected { 80 border-bottom-color : #fff; 81 z-index : 2; 82 background : #fff; 83 } 84 85 .tabs > div.selected:hover { 86 background : #fff; 87 cursor : default; 88 } 89 90 .tabs > div.selected:before { 91 box-shadow: 2px 2px 0 #fff; 92 } 93 94 .tabs > div.selected:after { 95 box-shadow: -2px 2px 0 #fff; 96 } 97 98 .tabs + .content { 99 margin-top : 10px; 100 padding : 10px 0 0 0; 101 }` 102 103 func init() { 104 style.Add(css) 105 } 106 107 // Tab represents a tab in a menu 108 type Tab struct { 109 // The name of the Tab 110 Name string 111 // Func to run when the tab is selected 112 Func func(dom.Element) 113 } 114 115 // New takes a list of tabs and generates a tabbed interface, which is return as a document fragment 116 func New(t []Tab) dom.Node { 117 f := xdom.DocumentFragment() 118 if len(t) < 0 { 119 return f 120 } 121 tabsDiv := xdom.Div() 122 contents := xdom.Div() 123 tabsDiv.Class().SetString("tabs") 124 contents.Class().SetString("content") 125 tabs := make([]dom.Element, len(t)) 126 for n := range t { 127 tabs[n] = xjs.SetInnerText(xdom.Div(), t[n].Name).(dom.Element) 128 tabs[n].AddEventListener("click", false, func() func(dom.Event) { 129 i := n 130 return func(e dom.Event) { 131 if tabs[i].Class().String() == "selected" { 132 return 133 } 134 for _, tab := range tabs { 135 tab.Class().Remove("selected") 136 } 137 newContents := contents.CloneNode(false).(*dom.HTMLDivElement) 138 contents.ParentNode().ReplaceChild(newContents, contents) 139 contents = newContents 140 tabs[i].Class().Add("selected") 141 go t[i].Func(contents) 142 } 143 }()) 144 tabsDiv.AppendChild(tabs[n]) 145 } 146 go t[0].Func(contents) 147 tabs[0].Class().Add("selected") 148 f.AppendChild(tabsDiv) 149 f.AppendChild(contents) 150 return f 151 }