view_common.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. package login
  2. import (
  3. "fmt"
  4. "net/http"
  5. v "github.com/qor5/ui/vuetify"
  6. "github.com/qor5/web"
  7. "github.com/qor5/x/login"
  8. . "github.com/theplant/htmlgo"
  9. )
  10. var DefaultViewCommon = &ViewCommon{
  11. WrapperClass: "d-flex pt-16 flex-column mx-auto",
  12. WrapperStyle: "max-width: 28rem;",
  13. TitleClass: "text-h5 mb-6 font-weight-bold",
  14. LabelClass: "d-block mb-1 grey--text text--darken-2 text-sm-body-2",
  15. }
  16. type ViewCommon struct {
  17. WrapperClass string
  18. WrapperStyle string
  19. TitleClass string
  20. LabelClass string
  21. }
  22. func (vc *ViewCommon) Notice(vh *login.ViewHelper, msgr *login.Messages, w http.ResponseWriter, r *http.Request) HTMLComponent {
  23. var nn HTMLComponent
  24. if n := vh.GetNoticeFlash(w, r); n != nil && n.Message != "" {
  25. switch n.Level {
  26. case login.NoticeLevel_Info:
  27. nn = vc.InfoNotice(n.Message)
  28. case login.NoticeLevel_Warn:
  29. nn = vc.WarnNotice(n.Message)
  30. case login.NoticeLevel_Error:
  31. nn = vc.ErrNotice(n.Message)
  32. }
  33. }
  34. return Components(
  35. vc.ErrNotice(vh.GetFailFlashMessage(msgr, w, r)),
  36. vc.WarnNotice(vh.GetWarnFlashMessage(msgr, w, r)),
  37. vc.InfoNotice(vh.GetInfoFlashMessage(msgr, w, r)),
  38. nn,
  39. )
  40. }
  41. func (vc *ViewCommon) ErrNotice(msg string) HTMLComponent {
  42. if msg == "" {
  43. return nil
  44. }
  45. return v.VAlert(Text(msg)).
  46. Dense(true).
  47. Class("text-center").
  48. Icon(false).
  49. Type("error")
  50. }
  51. func (vc *ViewCommon) WarnNotice(msg string) HTMLComponent {
  52. if msg == "" {
  53. return nil
  54. }
  55. return v.VAlert(Text(msg)).
  56. Dense(true).
  57. Class("text-center").
  58. Icon(false).
  59. Type("warning")
  60. }
  61. func (vc *ViewCommon) InfoNotice(msg string) HTMLComponent {
  62. if msg == "" {
  63. return nil
  64. }
  65. return v.VAlert(Text(msg)).
  66. Dense(true).
  67. Class("text-center").
  68. Icon(false).
  69. Type("info")
  70. }
  71. func (vc *ViewCommon) ErrorBody(msg string) HTMLComponent {
  72. return Div(
  73. Text(msg),
  74. )
  75. }
  76. func (vc *ViewCommon) Input(
  77. id string,
  78. placeholder string,
  79. val string,
  80. ) *v.VTextFieldBuilder {
  81. return v.VTextField().
  82. Attr("name", id).
  83. Id(id).
  84. Placeholder(placeholder).
  85. Value(val).
  86. Outlined(true).
  87. HideDetails(true).
  88. Dense(true)
  89. }
  90. func (vc *ViewCommon) PasswordInput(
  91. id string,
  92. placeholder string,
  93. val string,
  94. canReveal bool,
  95. ) *v.VTextFieldBuilder {
  96. in := vc.Input(id, placeholder, val)
  97. if canReveal {
  98. varName := fmt.Sprintf(`show_%s`, id)
  99. in.Attr(":append-icon", fmt.Sprintf(`vars.%s ? "visibility_off" : "visibility"`, varName)).
  100. Attr(":type", fmt.Sprintf(`vars.%s ? "text" : "password"`, varName)).
  101. Attr("@click:append", fmt.Sprintf(`vars.%s = !vars.%s`, varName, varName)).
  102. Attr(web.InitContextVars, fmt.Sprintf(`{%s: false}`, varName))
  103. }
  104. return in
  105. }
  106. // need to import zxcvbn js
  107. // func (vc *ViewCommon) PasswordInputWithStrengthMeter(in *v.VTextFieldBuilder, id string, val string) HTMLComponent {
  108. // passVar := fmt.Sprintf(`password_%s`, id)
  109. // meterScoreVar := fmt.Sprintf(`meter_score_%s`, id)
  110. // in.Attr("v-model", fmt.Sprintf(`vars.%s`, passVar)).
  111. // Attr(":loading", fmt.Sprintf(`!!vars.%s`, passVar)).
  112. // On("input", fmt.Sprintf(`vars.%s = vars.%s ? zxcvbn(vars.%s).score + 1 : 0`, meterScoreVar, passVar, passVar))
  113. // return Div(
  114. // in.Children(
  115. // RawHTML(fmt.Sprintf(`
  116. // <template v-slot:progress>
  117. // <v-progress-linear
  118. // :value="vars.%s * 20"
  119. // :color="['grey', 'red', 'deep-orange', 'amber', 'yellow', 'light-green'][vars.%s]"
  120. // absolute
  121. // ></v-progress-linear>
  122. // </template>
  123. // `, meterScoreVar, meterScoreVar)),
  124. // ),
  125. // ).Attr(web.InitContextVars, fmt.Sprintf(`{%s: "%s", %s: "%s" ? zxcvbn("%s").score + 1 : 0}`, passVar, val, meterScoreVar, val, val))
  126. // }
  127. // need to import zxcvbn.js
  128. func (vc *ViewCommon) PasswordInputWithStrengthMeter(in *v.VTextFieldBuilder, id string, val string) HTMLComponent {
  129. passVar := fmt.Sprintf(`password_%s`, id)
  130. meterScoreVar := fmt.Sprintf(`meter_score_%s`, id)
  131. in.Attr("v-model", fmt.Sprintf(`vars.%s`, passVar)).
  132. On("input", fmt.Sprintf(`vars.%s = vars.%s ? zxcvbn(vars.%s).score + 1 : 0`, meterScoreVar, passVar, passVar))
  133. return Div(
  134. in,
  135. v.VProgressLinear().
  136. Class("mt-2").
  137. Attr(":value", fmt.Sprintf(`vars.%s * 20`, meterScoreVar)).
  138. Attr(":color", fmt.Sprintf(`["grey", "red", "deep-orange", "amber", "yellow", "light-green"][vars.%s]`, meterScoreVar)).
  139. Attr("v-show", fmt.Sprintf(`!!vars.%s`, passVar)),
  140. ).Attr(web.InitContextVars, fmt.Sprintf(`{%s: "%s", %s: "%s" ? zxcvbn("%s").score + 1 : 0}`, passVar, val, meterScoreVar, val, val))
  141. }
  142. func (vc *ViewCommon) FormSubmitBtn(
  143. label string,
  144. ) *v.VBtnBuilder {
  145. return v.VBtn(label).
  146. Color("primary").
  147. Block(true).
  148. Large(true).
  149. Type("submit").
  150. Class("mt-6")
  151. }
  152. // requirements:
  153. // - submit button
  154. // - add class `g-recaptcha`
  155. // - add attr `data-sitekey=<key>`
  156. // - add attr `data-callback=onSubmit`
  157. //
  158. // - add token field like `Input("token").Id("token").Type("hidden")`
  159. func (vc *ViewCommon) InjectRecaptchaAssets(ctx *web.EventContext, formID string, tokenFieldID string) {
  160. ctx.Injector.HeadHTML(`
  161. <style>
  162. .grecaptcha-badge { visibility: hidden; }
  163. </style>
  164. `)
  165. ctx.Injector.HeadHTML(fmt.Sprintf(`
  166. <script>
  167. function onSubmit(token) {
  168. document.getElementById("%s").value = token;
  169. document.getElementById("%s").submit();
  170. }
  171. </script>
  172. `, tokenFieldID, formID))
  173. ctx.Injector.TailHTML(`
  174. <script src="https://www.google.com/recaptcha/api.js"></script>
  175. `)
  176. }
  177. func (vc *ViewCommon) InjectZxcvbn(ctx *web.EventContext) {
  178. ctx.Injector.HeadHTML(fmt.Sprintf(`
  179. <script src="%s"></script>
  180. `, login.ZxcvbnJSURL))
  181. }