WIP on htmx addition (#36)
- Remove custom tags/attrs now provided by Giraffe View Engine
This commit is contained in:
parent
accb65136c
commit
810b5d8258
|
@ -6,7 +6,7 @@ open PrayerTracker.ViewModels
|
|||
|
||||
/// View for the church edit page
|
||||
let edit (m : EditChurch) ctx vi =
|
||||
let pageTitle = match m.IsNew with true -> "Add a New Church" | false -> "Edit Church"
|
||||
let pageTitle = if m.IsNew then "Add a New Church" else "Edit Church"
|
||||
let s = I18N.localizer.Force ()
|
||||
[ form [ _action "/church/save"; _method "post"; _class "pt-center-columns" ] [
|
||||
style [ _scoped ] [
|
||||
|
|
|
@ -27,7 +27,7 @@ let space = rawText " "
|
|||
let icon name = i [ _class "material-icons" ] [ rawText name ]
|
||||
|
||||
/// Generate a Material Design icon, specifying the point size (must be defined in CSS)
|
||||
let iconSized size name = i [ _class $"material-icons md-{size}" ] [ rawText name ]
|
||||
let iconSized size name = i [ _class $"material-icons md-%i{size}" ] [ rawText name ]
|
||||
|
||||
/// Generate a CSRF prevention token
|
||||
let csrfToken (ctx : HttpContext) =
|
||||
|
@ -80,13 +80,11 @@ let namedColorList name selected attrs (s : IStringLocalizer) =
|
|||
|
||||
/// Generate an input[type=radio] that is selected if its value is the current value
|
||||
let radio name domId value current =
|
||||
input
|
||||
[ _type "radio"
|
||||
input [ _type "radio"
|
||||
_name name
|
||||
_id domId
|
||||
_value value
|
||||
if value = current then _checked
|
||||
]
|
||||
if value = current then _checked ]
|
||||
|
||||
/// Generate a select list with the current value selected
|
||||
let selectList name selected attrs items =
|
||||
|
@ -100,7 +98,7 @@ let selectList name selected attrs items =
|
|||
|> select (List.concat [ [ _name name; _id name ]; attrs ])
|
||||
|
||||
/// Generate the text for a default entry at the top of a select list
|
||||
let selectDefault text = $"— {text} —"
|
||||
let selectDefault text = $"— %s{text} —"
|
||||
|
||||
/// Generate a standard submit button with icon and text
|
||||
let submit attrs ico text = button (_type "submit" :: attrs) [ icon ico; rawText " "; locStr text ]
|
||||
|
@ -108,29 +106,13 @@ let submit attrs ico text = button (_type "submit" :: attrs) [ icon ico; rawText
|
|||
|
||||
open System
|
||||
|
||||
// TODO: this is where to implement issue #1
|
||||
/// Format a GUID with no dashes (used for URLs and forms)
|
||||
let flatGuid (x : Guid) = x.ToString "N"
|
||||
|
||||
/// An empty GUID string (used for "add" actions)
|
||||
let emptyGuid = flatGuid Guid.Empty
|
||||
|
||||
|
||||
/// blockquote tag
|
||||
let blockquote = tag "blockquote"
|
||||
|
||||
/// role attribute
|
||||
let _role = attr "role"
|
||||
/// aria-* attribute
|
||||
let _aria typ = attr $"aria-{typ}"
|
||||
/// onclick attribute
|
||||
let _onclick = attr "onclick"
|
||||
/// onsubmit attribute
|
||||
let _onsubmit = attr "onsubmit"
|
||||
|
||||
/// scoped flag (used for <style> tag)
|
||||
let _scoped = flag "scoped"
|
||||
|
||||
|
||||
/// The name this function used to have when the view engine was part of Giraffe
|
||||
let renderHtmlNode = RenderView.AsString.htmlNode
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
module PrayerTracker.Views.Layout
|
||||
|
||||
open Giraffe.ViewEngine
|
||||
open Giraffe.ViewEngine.Accessibility
|
||||
open Giraffe.ViewEngine.Htmx
|
||||
open PrayerTracker
|
||||
open PrayerTracker.ViewModels
|
||||
open System
|
||||
|
@ -12,6 +14,16 @@ open System.Globalization
|
|||
let langCode () = if CultureInfo.CurrentCulture.Name.StartsWith "es" then "es" else "en"
|
||||
|
||||
|
||||
/// Known htmx targets
|
||||
module Target =
|
||||
|
||||
/// htmx links target the body element
|
||||
let body = _hxTarget "body"
|
||||
|
||||
/// htmx links target the #pt-body element
|
||||
let content = _hxTarget "#pt-body"
|
||||
|
||||
|
||||
/// Navigation items
|
||||
module Navigation =
|
||||
|
||||
|
@ -23,39 +35,46 @@ module Navigation =
|
|||
match m.User with
|
||||
| Some u ->
|
||||
li [ _class "dropdown" ] [
|
||||
a [ _class "dropbtn"
|
||||
_role "button"
|
||||
_aria "label" s["Requests"].Value
|
||||
_title s["Requests"].Value ]
|
||||
[ icon "question_answer"; space; locStr s["Requests"]; space; icon "keyboard_arrow_down" ]
|
||||
div [ _class "dropdown-content"; _role "menu" ] [
|
||||
a [ _href "/prayer-requests" ] [ icon "compare_arrows"; menuSpacer; locStr s["Maintain"] ]
|
||||
a [ _href "/prayer-requests/view" ] [ icon "list"; menuSpacer; locStr s["View List"] ]
|
||||
a [ _class "dropbtn"; _ariaLabel s["Requests"].Value; _title s["Requests"].Value; _roleButton ] [
|
||||
icon "question_answer"; space; locStr s["Requests"]; space; icon "keyboard_arrow_down"
|
||||
]
|
||||
div [ _class "dropdown-content"; _roleMenuBar ] [
|
||||
a [ _href "/prayer-requests"; _roleMenuItem ] [
|
||||
icon "compare_arrows"; menuSpacer; locStr s["Maintain"]
|
||||
]
|
||||
a [ _href "/prayer-requests/view"; _roleMenuItem ] [
|
||||
icon "list"; menuSpacer; locStr s["View List"]
|
||||
]
|
||||
]
|
||||
]
|
||||
li [ _class "dropdown" ] [
|
||||
a [ _class "dropbtn"; _role "button"; _aria "label" s["Group"].Value; _title s["Group"].Value ]
|
||||
[ icon "group"; space; locStr s["Group"]; space; icon "keyboard_arrow_down" ]
|
||||
div [ _class "dropdown-content"; _role "menu" ] [
|
||||
a [ _href "/small-group/members" ]
|
||||
[ icon "email"; menuSpacer; locStr s["Maintain Group Members"] ]
|
||||
a [ _href "/small-group/announcement" ]
|
||||
[ icon "send"; menuSpacer; locStr s["Send Announcement"] ]
|
||||
a [ _href "/small-group/preferences" ]
|
||||
[ icon "build"; menuSpacer; locStr s["Change Preferences"] ]
|
||||
a [ _class "dropbtn"; _ariaLabel s["Group"].Value; _title s["Group"].Value; _roleButton ] [
|
||||
icon "group"; space; locStr s["Group"]; space; icon "keyboard_arrow_down"
|
||||
]
|
||||
div [ _class "dropdown-content"; _roleMenuBar ] [
|
||||
a [ _href "/small-group/members"; _roleMenuItem ] [
|
||||
icon "email"; menuSpacer; locStr s["Maintain Group Members"]
|
||||
]
|
||||
a [ _href "/small-group/announcement"; _roleMenuItem ] [
|
||||
icon "send"; menuSpacer; locStr s["Send Announcement"]
|
||||
]
|
||||
a [ _href "/small-group/preferences"; _roleMenuItem ] [
|
||||
icon "build"; menuSpacer; locStr s["Change Preferences"]
|
||||
]
|
||||
]
|
||||
]
|
||||
if u.isAdmin then
|
||||
li [ _class "dropdown" ] [
|
||||
a [ _class "dropbtn"
|
||||
_role "button"
|
||||
_aria "label" s["Administration"].Value
|
||||
_ariaLabel s["Administration"].Value
|
||||
_title s["Administration"].Value
|
||||
] [ icon "settings"; space; locStr s["Administration"]; space; icon "keyboard_arrow_down" ]
|
||||
div [ _class "dropdown-content"; _role "menu" ] [
|
||||
a [ _href "/churches" ] [ icon "home"; menuSpacer; locStr s["Churches"] ]
|
||||
a [ _href "/small-groups" ] [ icon "send"; menuSpacer; locStr s["Groups"] ]
|
||||
a [ _href "/users" ] [ icon "build"; menuSpacer; locStr s["Users"] ]
|
||||
_roleButton ] [
|
||||
icon "settings"; space; locStr s["Administration"]; space; icon "keyboard_arrow_down"
|
||||
]
|
||||
div [ _class "dropdown-content"; _roleMenuBar ] [
|
||||
a [ _href "/churches"; _roleMenuItem ] [ icon "home"; menuSpacer; locStr s["Churches"] ]
|
||||
a [ _href "/small-groups"; _roleMenuItem ] [ icon "send"; menuSpacer; locStr s["Groups"] ]
|
||||
a [ _href "/users"; _roleMenuItem ] [ icon "build"; menuSpacer; locStr s["Users"] ]
|
||||
]
|
||||
]
|
||||
| None ->
|
||||
|
@ -63,63 +82,72 @@ module Navigation =
|
|||
| Some _ ->
|
||||
li [] [
|
||||
a [ _href "/prayer-requests/view"
|
||||
_aria "label" s["View Request List"].Value
|
||||
_title s["View Request List"].Value
|
||||
] [ icon "list"; space; locStr s["View Request List"] ]
|
||||
_ariaLabel s["View Request List"].Value
|
||||
_title s["View Request List"].Value ] [
|
||||
icon "list"; space; locStr s["View Request List"]
|
||||
]
|
||||
]
|
||||
| None ->
|
||||
li [ _class "dropdown" ] [
|
||||
a [ _class "dropbtn"
|
||||
_role "button"
|
||||
_aria "label" s["Log On"].Value
|
||||
_title s["Log On"].Value
|
||||
] [ icon "security"; space; locStr s["Log On"]; space; icon "keyboard_arrow_down" ]
|
||||
div [ _class "dropdown-content"; _role "menu" ] [
|
||||
a [ _href "/user/log-on" ] [ icon "person"; menuSpacer; locStr s["User"] ]
|
||||
a [ _href "/small-group/log-on" ] [ icon "group"; menuSpacer; locStr s["Group"] ]
|
||||
a [ _class "dropbtn"; _ariaLabel s["Log On"].Value; _title s["Log On"].Value; _roleButton ] [
|
||||
icon "security"; space; locStr s["Log On"]; space; icon "keyboard_arrow_down"
|
||||
]
|
||||
div [ _class "dropdown-content"; _roleMenuBar ] [
|
||||
a [ _href "/user/log-on"; _roleMenuItem ] [ icon "person"; menuSpacer; locStr s["User"] ]
|
||||
a [ _href "/small-group/log-on"; _roleMenuItem ] [
|
||||
icon "group"; menuSpacer; locStr s["Group"]
|
||||
]
|
||||
]
|
||||
]
|
||||
li [] [
|
||||
a [ _href "/prayer-requests/lists"
|
||||
_aria "label" s["View Request List"].Value
|
||||
_title s["View Request List"].Value
|
||||
] [ icon "list"; space; locStr s["View Request List"] ]
|
||||
_ariaLabel s["View Request List"].Value
|
||||
_title s["View Request List"].Value ] [
|
||||
icon "list"; space; locStr s["View Request List"]
|
||||
]
|
||||
]
|
||||
li [] [
|
||||
a [ _href $"https://docs.prayer.bitbadger.solutions/{langCode ()}"
|
||||
_aria "label" s["Help"].Value;
|
||||
_ariaLabel s["Help"].Value
|
||||
_title s["View Help"].Value
|
||||
_target "_blank"
|
||||
] [ icon "help"; space; locStr s["Help"] ]
|
||||
_rel "noopener" ] [
|
||||
icon "help"; space; locStr s["Help"]
|
||||
]
|
||||
]
|
||||
]
|
||||
let rightLinks =
|
||||
match m.Group with
|
||||
| Some _ -> [
|
||||
match m.User with
|
||||
| Some _ ->
|
||||
[ match m.User with
|
||||
| Some _ ->
|
||||
li [] [
|
||||
a [ _href "/user/password"
|
||||
_aria "label" s["Change Your Password"].Value
|
||||
_title s["Change Your Password"].Value
|
||||
] [ icon "lock"; space; locStr s["Change Your Password"] ]
|
||||
_ariaLabel s["Change Your Password"].Value
|
||||
_title s["Change Your Password"].Value ] [
|
||||
icon "lock"; space; locStr s["Change Your Password"]
|
||||
]
|
||||
]
|
||||
| None -> ()
|
||||
li [] [
|
||||
a [ _href "/log-off"; _aria "label" s["Log Off"].Value; _title s["Log Off"].Value ]
|
||||
[ icon "power_settings_new"; space; locStr s["Log Off"] ]
|
||||
a [ _href "/log-off"
|
||||
_ariaLabel s["Log Off"].Value
|
||||
_title s["Log Off"].Value
|
||||
_hxTarget "body" ] [
|
||||
icon "power_settings_new"; space; locStr s["Log Off"]
|
||||
]
|
||||
]
|
||||
]
|
||||
| None -> []
|
||||
header [ _class "pt-title-bar" ] [
|
||||
section [ _class "pt-title-bar-left" ] [
|
||||
header [ _class "pt-title-bar"; Target.content ] [
|
||||
section [ _class "pt-title-bar-left"; _ariaLabel "Left side of top menu" ] [
|
||||
span [ _class "pt-title-bar-home" ] [
|
||||
a [ _href "/"; _title s["Home"].Value ] [ locStr s["PrayerTracker"] ]
|
||||
]
|
||||
ul [] leftLinks
|
||||
]
|
||||
section [ _class "pt-title-bar-center" ] []
|
||||
section [ _class "pt-title-bar-right"; _role "toolbar" ] [
|
||||
section [ _class "pt-title-bar-center"; _ariaLabel "Empty center space in top menu" ] []
|
||||
section [ _class "pt-title-bar-right"; _roleToolBar; _ariaLabel "Right side of top menu" ] [
|
||||
ul [] rightLinks
|
||||
]
|
||||
]
|
||||
|
@ -127,7 +155,7 @@ module Navigation =
|
|||
/// Identity bar (below top nav)
|
||||
let identity m =
|
||||
let s = I18N.localizer.Force ()
|
||||
header [ _id "pt-language" ] [
|
||||
header [ _id "pt-language"; Target.body ] [
|
||||
div [] [
|
||||
span [ _class "u" ] [ locStr s["Language"]; rawText ": " ]
|
||||
match langCode () with
|
||||
|
@ -141,8 +169,8 @@ module Navigation =
|
|||
a [ _href "/language/es" ] [ locStr s["Cambie a Español"] ]
|
||||
]
|
||||
match m.Group with
|
||||
| Some g ->[
|
||||
match m.User with
|
||||
| Some g ->
|
||||
[ match m.User with
|
||||
| Some u ->
|
||||
span [ _class "u" ] [ locStr s["Currently Logged On"] ]
|
||||
rawText " "
|
||||
|
@ -155,7 +183,7 @@ module Navigation =
|
|||
icon "group"
|
||||
space
|
||||
match m.User with
|
||||
| Some _ -> a [ _href "/small-group" ] [ strong [] [ str g.name ] ]
|
||||
| Some _ -> a [ _href "/small-group"; Target.content ] [ strong [] [ str g.name ] ]
|
||||
| None -> strong [] [ str g.name ]
|
||||
rawText " "
|
||||
]
|
||||
|
@ -178,12 +206,11 @@ module Content =
|
|||
let private titleSep = rawText " « "
|
||||
|
||||
/// Common HTML head tag items
|
||||
let private commonHead =
|
||||
[ meta [ _name "viewport"; _content "width=device-width, initial-scale=1" ]
|
||||
let private commonHead = [
|
||||
meta [ _name "viewport"; _content "width=device-width, initial-scale=1" ]
|
||||
meta [ _name "generator"; _content "Giraffe" ]
|
||||
link [ _rel "stylesheet"; _href "https://fonts.googleapis.com/icon?family=Material+Icons" ]
|
||||
link [ _rel "stylesheet"; _href "/css/app.css" ]
|
||||
script [ _src "/js/app.js" ] []
|
||||
]
|
||||
|
||||
/// Render the <head> portion of the page
|
||||
|
@ -203,8 +230,12 @@ let private htmlHead m pageTitle =
|
|||
let private helpLink link =
|
||||
let s = I18N.localizer.Force ()
|
||||
sup [] [
|
||||
a [ _href link; _title s["Click for Help on This Page"].Value; _onclick $"return PT.showHelp('{link}')" ]
|
||||
[ icon "help_outline" ]
|
||||
a [ _href link
|
||||
_title s["Click for Help on This Page"].Value
|
||||
_onclick $"return PT.showHelp('{link}')"
|
||||
_hxNoBoost ] [
|
||||
icon "help_outline"
|
||||
]
|
||||
]
|
||||
|
||||
/// Render the page title, and optionally a help link
|
||||
|
@ -240,7 +271,7 @@ let private messages m =
|
|||
/// Render the <footer> at the bottom of the page
|
||||
let private htmlFooter m =
|
||||
let s = I18N.localizer.Force ()
|
||||
let imgText = sprintf "%O %O" s["PrayerTracker"] s["from Bit Badger Solutions"]
|
||||
let imgText = $"""%O{s["PrayerTracker"]} %O{s["from Bit Badger Solutions"]}"""
|
||||
let resultTime = TimeSpan(DateTime.Now.Ticks - m.RequestStart).TotalSeconds
|
||||
footer [] [
|
||||
div [ _id "pt-legal" ] [
|
||||
|
@ -251,27 +282,31 @@ let private htmlFooter m =
|
|||
a [ _href "https://github.com/bit-badger/PrayerTracker"
|
||||
_title s["View source code and get technical support"].Value
|
||||
_target "_blank"
|
||||
_rel "noopener"
|
||||
] [ locStr s["Source & Support"]
|
||||
_rel "noopener" ] [
|
||||
locStr s["Source & Support"]
|
||||
]
|
||||
]
|
||||
div [ _id "pt-footer" ] [
|
||||
a [ _href "/"; _style "line-height:28px;" ]
|
||||
[ img [ _src $"""/img/%O{s["footer_en"]}.png"""; _alt imgText; _title imgText ] ]
|
||||
a [ _href "/"; _style "line-height:28px;" ] [
|
||||
img [ _src $"""/img/%O{s["footer_en"]}.png"""; _alt imgText; _title imgText ]
|
||||
]
|
||||
str m.Version
|
||||
space
|
||||
i [ _title s["This page loaded in {0:N3} seconds", resultTime].Value; _class "material-icons md-18" ]
|
||||
[ str "schedule" ]
|
||||
i [ _title s["This page loaded in {0:N3} seconds", resultTime].Value; _class "material-icons md-18" ] [
|
||||
str "schedule"
|
||||
]
|
||||
]
|
||||
Script.minified
|
||||
script [ _src "/js/app.js" ] []
|
||||
]
|
||||
|
||||
/// The standard layout for PrayerTracker
|
||||
let standard m pageTitle (content : XmlNode) =
|
||||
let s = I18N.localizer.Force ()
|
||||
let ttl = s[pageTitle]
|
||||
html [ _lang "" ] [
|
||||
html [ _lang (langCode ()) ] [
|
||||
htmlHead m ttl
|
||||
body [] [
|
||||
body [ _hxBoost ] [
|
||||
Navigation.top m
|
||||
div [ _id "pt-body" ] [
|
||||
Navigation.identity m
|
||||
|
@ -287,7 +322,7 @@ let standard m pageTitle (content : XmlNode) =
|
|||
let bare pageTitle content =
|
||||
let s = I18N.localizer.Force ()
|
||||
let ttl = s[pageTitle]
|
||||
html [ _lang "" ] [
|
||||
html [ _lang (langCode ()) ] [
|
||||
head [] [
|
||||
meta [ _charset "UTF-8" ]
|
||||
title [] [ locStr ttl; titleSep; locStr s["PrayerTracker"] ]
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Giraffe" Version="6.0.0" />
|
||||
<PackageReference Include="Giraffe.ViewEngine" Version="1.4.0" />
|
||||
<PackageReference Include="Giraffe.ViewEngine.Htmx" Version="1.8.0" />
|
||||
<PackageReference Include="MailKit" Version="3.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Html.Abstractions" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
|
||||
|
|
Loading…
Reference in New Issue
Block a user