pgenr.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. package pgenr
  2. import (
  3. "bytes"
  4. "golang.org/x/net/html/atom"
  5. "html"
  6. "sync"
  7. )
  8. const (
  9. TextThemeSuccess = "success"
  10. TextThemeDanger = "danger"
  11. )
  12. var (
  13. bufferPool sync.Pool
  14. )
  15. func getBuffer() *bytes.Buffer {
  16. if v := bufferPool.Get(); v == nil {
  17. return new(bytes.Buffer)
  18. } else {
  19. return v.(*bytes.Buffer)
  20. }
  21. }
  22. func releaseBuffer(sb *bytes.Buffer) {
  23. sb.Reset()
  24. bufferPool.Put(sb)
  25. }
  26. type (
  27. Element interface {
  28. Html() string
  29. }
  30. TextOption func(t *Text)
  31. ButtonOption func(btn *Button)
  32. Text struct {
  33. Tag atom.Atom
  34. Content string
  35. Theme string
  36. Color string
  37. Style map[string]string
  38. }
  39. Button struct {
  40. Url string
  41. Text string
  42. Color string
  43. Style map[string]string
  44. }
  45. Entry struct {
  46. Title Element
  47. Items map[string]Element
  48. }
  49. Action struct {
  50. Instructions Element
  51. Button *Button
  52. InviteCode string
  53. }
  54. Table struct {
  55. Title Element
  56. Header []Element
  57. Body [][]Element
  58. }
  59. Page struct {
  60. Title string
  61. Head string
  62. Intros []Element
  63. Entries []*Entry
  64. Actions []*Action
  65. Tables []*Table
  66. Outros []Element
  67. Copyright string
  68. }
  69. )
  70. func (a *Action) Html() string {
  71. return a.String()
  72. }
  73. func (a *Action) String() string {
  74. br := getBuffer()
  75. defer releaseBuffer(br)
  76. if a.Instructions != nil {
  77. br.WriteString(a.Instructions.Html())
  78. }
  79. br.WriteString("<div class='action-block'>")
  80. if a.InviteCode != "" {
  81. br.WriteString("<span class='invite-code'>")
  82. br.WriteString(html.EscapeString(a.InviteCode))
  83. br.WriteString("</span>")
  84. }
  85. if a.Button != nil {
  86. br.WriteString(a.Button.String())
  87. }
  88. br.WriteString("</div>")
  89. return br.String()
  90. }
  91. func (e *Entry) Html() string {
  92. return e.String()
  93. }
  94. func (e *Entry) String() string {
  95. br := getBuffer()
  96. defer releaseBuffer(br)
  97. br.WriteString("<div class='grid'>")
  98. if e.Title != nil {
  99. br.WriteString(e.Title.Html())
  100. }
  101. for k, v := range e.Items {
  102. br.WriteString("<div class='row'>")
  103. br.WriteString("<div class='preview-label'>")
  104. br.WriteString(k)
  105. br.WriteString("</div>")
  106. br.WriteString("<div class='preview-value'>")
  107. br.WriteString(v.Html())
  108. br.WriteString("</div>")
  109. br.WriteString("</div>")
  110. }
  111. br.WriteString("</div>")
  112. return br.String()
  113. }
  114. func (e *Entry) AddItem(label string, txt *Text) *Entry {
  115. if e.Items == nil {
  116. e.Items = make(map[string]Element)
  117. }
  118. e.Items[label] = txt
  119. return e
  120. }
  121. func (page *Page) SetHead(s string) *Page {
  122. page.Head = s
  123. return page
  124. }
  125. func (page *Page) SetCopyright(s string) *Page {
  126. page.Copyright = s
  127. return page
  128. }
  129. func (page *Page) AddPlainIntro(s string) *Page {
  130. page.Intros = append(page.Intros, NewText(s, WithTextTag(atom.P)))
  131. return page
  132. }
  133. func (page *Page) AddIntro(t *Text) *Page {
  134. page.Intros = append(page.Intros, t)
  135. return page
  136. }
  137. func (page *Page) AddEntry(e *Entry) *Page {
  138. page.Entries = append(page.Entries, e)
  139. return page
  140. }
  141. func (page *Page) AddTable(t *Table) *Page {
  142. page.Tables = append(page.Tables, t)
  143. return page
  144. }
  145. func (page *Page) AddButtonAction(s string, btn *Button) *Page {
  146. page.Actions = append(page.Actions, &Action{Instructions: NewText(s, WithTextTag(atom.P)), Button: btn})
  147. return page
  148. }
  149. func (page *Page) AddInviteCodeAction(s string, code string) *Page {
  150. page.Actions = append(page.Actions, &Action{Instructions: NewText(s, WithTextTag(atom.P)), InviteCode: code})
  151. return page
  152. }
  153. func (page *Page) AddOutro(t *Text) *Page {
  154. page.Outros = append(page.Outros, t)
  155. return page
  156. }
  157. func (page *Page) AddPlainOutro(s string) *Page {
  158. page.Outros = append(page.Outros, NewText(s, WithTextTag(atom.P)))
  159. return page
  160. }
  161. func (page *Page) Escape() {
  162. page.Head = html.EscapeString(page.Head)
  163. page.Copyright = html.EscapeString(page.Copyright)
  164. }
  165. func NewPage(title string) *Page {
  166. return &Page{
  167. Title: title,
  168. Intros: make([]Element, 0),
  169. Entries: make([]*Entry, 0),
  170. Actions: make([]*Action, 0),
  171. Outros: make([]Element, 0),
  172. Tables: make([]*Table, 0),
  173. }
  174. }
  175. func NewEntry(title string) *Entry {
  176. return &Entry{
  177. Title: NewText(title, WithTextTag(atom.P)),
  178. Items: make(map[string]Element),
  179. }
  180. }
  181. func WithTextTheme(s string) TextOption {
  182. return func(t *Text) {
  183. t.Theme = s
  184. }
  185. }
  186. func WithTextStyle(ms map[string]string) TextOption {
  187. return func(t *Text) {
  188. t.Style = ms
  189. }
  190. }
  191. func WithTextTag(tag atom.Atom) TextOption {
  192. return func(t *Text) {
  193. t.Tag = tag
  194. }
  195. }
  196. func WithTextColor(color string) TextOption {
  197. return func(t *Text) {
  198. t.Color = color
  199. }
  200. }
  201. func (button *Button) Html() string {
  202. return button.String()
  203. }
  204. func (button *Button) String() string {
  205. br := getBuffer()
  206. defer releaseBuffer(br)
  207. if button.Style == nil {
  208. button.Style = make(map[string]string)
  209. }
  210. if button.Color != "" {
  211. button.Style["color"] = button.Color
  212. }
  213. br.WriteString("<a class='button'")
  214. if button.Url != "" {
  215. br.WriteString(" href='" + button.Url + "'")
  216. }
  217. if len(button.Style) > 0 {
  218. br.WriteString(" style='")
  219. for k, v := range button.Style {
  220. br.WriteString(k)
  221. br.WriteString(":")
  222. br.WriteString(v)
  223. br.WriteString(";")
  224. }
  225. br.WriteString("'")
  226. }
  227. br.WriteString(">")
  228. br.WriteString(html.EscapeString(button.Text))
  229. br.WriteString("</a>")
  230. return br.String()
  231. }
  232. func (table *Table) SetHead(elements ...Element) {
  233. table.Header = elements
  234. }
  235. func (table *Table) AddCell(elements ...Element) {
  236. if table.Body == nil {
  237. table.Body = make([][]Element, 0)
  238. }
  239. table.Body = append(table.Body, elements)
  240. }
  241. func (table *Table) String() string {
  242. br := getBuffer()
  243. defer releaseBuffer(br)
  244. br.WriteString("<div class='table-wrapper'>")
  245. if table.Title != nil {
  246. br.WriteString("<div class='table-title'>")
  247. br.WriteString(table.Title.Html())
  248. br.WriteString("</div>")
  249. }
  250. br.WriteString("<table class='table'>")
  251. if len(table.Header) > 0 {
  252. br.WriteString("<thead><tr>")
  253. for _, text := range table.Header {
  254. br.WriteString("<th>")
  255. br.WriteString(text.Html())
  256. br.WriteString("</th>")
  257. }
  258. br.WriteString("</tr></thead>")
  259. }
  260. if len(table.Body) > 0 {
  261. br.WriteString("<tbody>")
  262. for _, cell := range table.Body {
  263. br.WriteString("<tr>")
  264. for _, text := range cell {
  265. br.WriteString("<td>")
  266. br.WriteString(text.Html())
  267. br.WriteString("</td>")
  268. }
  269. br.WriteString("</tr>")
  270. }
  271. br.WriteString("</tbody>")
  272. }
  273. br.WriteString("</table></div>")
  274. return br.String()
  275. }
  276. func (text *Text) Html() string {
  277. return text.String()
  278. }
  279. func (text *Text) String() string {
  280. br := getBuffer()
  281. defer releaseBuffer(br)
  282. if text.Style == nil {
  283. text.Style = make(map[string]string)
  284. }
  285. if text.Color != "" {
  286. text.Style["color"] = text.Color
  287. }
  288. if text.Tag == 0 {
  289. text.Tag = atom.Span
  290. }
  291. br.WriteString("<")
  292. br.WriteString(text.Tag.String())
  293. if text.Theme != "" {
  294. br.WriteString(" class='text-")
  295. br.WriteString(text.Theme)
  296. br.WriteString("'")
  297. }
  298. if len(text.Style) > 0 {
  299. br.WriteString(" style='")
  300. for k, v := range text.Style {
  301. br.WriteString(k)
  302. br.WriteString(":")
  303. br.WriteString(v)
  304. br.WriteString(";")
  305. }
  306. br.WriteString("'")
  307. }
  308. br.WriteString(">")
  309. br.WriteString(html.EscapeString(text.Content))
  310. br.WriteString("</")
  311. br.WriteString(text.Tag.String())
  312. br.WriteString(">")
  313. return br.String()
  314. }
  315. func NewButton(label, link string, opts ...ButtonOption) *Button {
  316. btn := &Button{Text: label, Url: link}
  317. for _, cb := range opts {
  318. cb(btn)
  319. }
  320. return btn
  321. }
  322. func NewText(s string, opts ...TextOption) *Text {
  323. txt := &Text{Content: s, Tag: atom.Span}
  324. for _, cb := range opts {
  325. cb(txt)
  326. }
  327. return txt
  328. }
  329. func NewTable(title *Text) *Table {
  330. table := &Table{
  331. Title: title,
  332. Header: make([]Element, 0),
  333. Body: make([][]Element, 0),
  334. }
  335. return table
  336. }