diff options
-rw-r--r-- | plugins/caldav/routes.go | 60 | ||||
-rw-r--r-- | themes/alps/assets/style.css | 101 | ||||
-rw-r--r-- | themes/alps/calendar-header.html | 18 | ||||
-rw-r--r-- | themes/alps/calendar.html | 84 |
4 files changed, 262 insertions, 1 deletions
diff --git a/plugins/caldav/routes.go b/plugins/caldav/routes.go index cb8aa2a..d7c31f7 100644 --- a/plugins/caldav/routes.go +++ b/plugins/caldav/routes.go @@ -18,9 +18,16 @@ import ( type CalendarRenderData struct { alps.BaseRenderData Time time.Time + Now time.Time + Dates [7 * 6]time.Time Calendar *caldav.Calendar Events []CalendarObject PrevPage, NextPage string + PrevTime, NextTime time.Time + + EventsForDate func(time.Time) []CalendarObject + DaySuffix func(n int) string + Sub func(a, b int) int } type EventRenderData struct { @@ -96,13 +103,66 @@ func registerRoutes(p *alps.GoPlugin, u *url.URL) { return fmt.Errorf("failed to query calendar: %v", err) } + // TODO: Time zones are hard + var dates [7 * 6]time.Time + initialDate := start.UTC() + initialDate = initialDate.AddDate(0, 0, -int(initialDate.Weekday())) + for i := 0; i < len(dates); i += 1 { + dates[i] = initialDate + initialDate = initialDate.AddDate(0, 0, 1) + } + + eventMap := make(map[time.Time][]CalendarObject) + for _, ev := range events { + ev := ev // make a copy + // TODO: include event on each date for which it is active + co := ev.Data.Events()[0] + startTime, _ := co.DateTimeStart(nil) + startTime = startTime.UTC().Truncate(time.Hour * 24) + eventMap[startTime] = append(eventMap[startTime], CalendarObject{&ev}) + } + return ctx.Render(http.StatusOK, "calendar.html", &CalendarRenderData{ BaseRenderData: *alps.NewBaseRenderData(ctx), Time: start, + Now: time.Now(), // TODO: Use client time zone Calendar: calendar, + Dates: dates, Events: newCalendarObjectList(events), PrevPage: start.AddDate(0, -1, 0).Format(monthPageLayout), NextPage: start.AddDate(0, 1, 0).Format(monthPageLayout), + PrevTime: start.AddDate(0, -1, 0), + NextTime: start.AddDate(0, 1, 0), + + EventsForDate: func(when time.Time) []CalendarObject { + if events, ok := eventMap[when.Truncate(time.Hour * 24)]; ok { + return events + } + return nil + }, + + DaySuffix: func(n int) string { + if n % 100 >= 11 && n % 100 <= 13 { + return "th" + } + return map[int]string{ + 0: "th", + 1: "st", + 2: "nd", + 3: "rd", + 4: "th", + 5: "th", + 6: "th", + 7: "th", + 8: "th", + 9: "th", + }[n % 10] + }, + + Sub: func (a, b int) int { + // Why isn't this built-in, come on Go + return a - b + }, }) }) diff --git a/themes/alps/assets/style.css b/themes/alps/assets/style.css index c62fbbc..8ce6549 100644 --- a/themes/alps/assets/style.css +++ b/themes/alps/assets/style.css @@ -261,6 +261,11 @@ main.new-contact .actions > *:not(:last-child) { margin-right: 1rem; } +.actions h3 { + align-self: center; + margin: 0 1.3rem 0 1rem; +} + .message-list-subject a { color: #77c; } .message-list-unread.message-list-sender, @@ -370,7 +375,7 @@ main table tfoot { padding: 0.3rem 0.5rem; } -.actions-pagination .button-link:first-child:not(:last-child) { +.actions-pagination .button-link:not(:last-child) { margin-right: 0.3rem; } @@ -444,6 +449,100 @@ main table tfoot { margin-top: 1rem; } +main.calendar .dates { + flex-grow: 1; + padding: 0.3rem; + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr; + grid-template-rows: auto 1fr 1fr 1fr 1fr 1fr 1fr auto; + grid-template-areas: + "sunday-top monday-top wednesday-top tuesday-top thursday-top friday-top saturday-top" + "dates dates dates dates dates dates dates" + "dates dates dates dates dates dates dates" + "dates dates dates dates dates dates dates" + "dates dates dates dates dates dates dates" + "dates dates dates dates dates dates dates" + "dates dates dates dates dates dates dates" + "sunday-bottom monday-bottom wednesday-bottom tuesday-bottom thursday-bottom friday-bottom saturday-bottom"; + grid-gap: 0.3rem; +} + +main.calendar .dates .weekday { + text-align: center; + font-size: 1.1rem; + font-weight: normal; +} + +main.calendar .dates .sunday-top { grid-area: sunday-top; } +main.calendar .dates .monday-top { grid-area: monday-top; } +main.calendar .dates .tuesday-top { grid-area: tuesday-top; } +main.calendar .dates .wednesday-top { grid-area: wednesday-top; } +main.calendar .dates .thursday-top { grid-area: thursday-top; } +main.calendar .dates .friday-top { grid-area: friday-top; } +main.calendar .dates .saturday-top { grid-area: saturday-top; } +main.calendar .dates .sunday-bottom { grid-area: sunday-bottom; } +main.calendar .dates .monday-bottom { grid-area: monday-bottom; } +main.calendar .dates .tuesday-bottom { grid-area: tuesday-bottom; } +main.calendar .dates .wednesday-bottom { grid-area: wednesday-bottom; } +main.calendar .dates .thursday-bottom { grid-area: thursday-bottom; } +main.calendar .dates .friday-bottom { grid-area: friday-bottom; } +main.calendar .dates .saturday-bottom { grid-area: saturday-bottom; } + +main.calendar .date { + border: 1px solid #eee; + padding: 0.3rem; + background: white; + display: flex; + flex-direction: column; + position: relative; +} + +main.calendar .date.active { + background-color: #f6fff6; + border: 1px solid #afa; +} + +main.calendar .date .date-link { + position: absolute; + top: 0; right: 0; bottom: 0; left: 0; +} + +main.calendar .date.extra { + background: transparent; + border: none; +} + +main.calendar .date ul { + list-style: none; + margin: 0; + padding-left: 0.3rem; +} + +main.calendar .date .events { + flex-grow: 1; +} + +main.calendar .date.extra .events { + visibility: hidden; +} + +main.calendar .events .start-time { + color: #444; +} + +main.calendar .events .overflow { + color: #444; + text-align: right; +} + +main.calendar .date h4 { + font-weight: normal; + text-align: right; + color: #666; +} + +main.calendar .date h4 .da { font-size: 1.2rem; } + input[type="submit"], .button, button, diff --git a/themes/alps/calendar-header.html b/themes/alps/calendar-header.html new file mode 100644 index 0000000..d902806 --- /dev/null +++ b/themes/alps/calendar-header.html @@ -0,0 +1,18 @@ +<div class="actions-wrap"> + <div class="actions-pagination" style="margin-left: 0;"> + <a href="?month={{.PrevPage}}" class="button-link">« {{.PrevTime.Format "January"}}</a> + <h3>{{.Time.Format "January 2006"}}</h3> + <a href="?month={{.NextPage}}" class="button-link">{{.NextTime.Format "January"}} »</a> + {{if ne .Time.Month .Now.Month}} + <a href="/calendar" class="button-link">Today »</a> + {{end}} + </div> + + <form method="get" class="actions-search action-group"> + <input + type="text" + name="query" + placeholder="Search {{.Calendar.Name}} events..."> + <button>Search</button> + </form> +</div> diff --git a/themes/alps/calendar.html b/themes/alps/calendar.html new file mode 100644 index 0000000..5bdd6eb --- /dev/null +++ b/themes/alps/calendar.html @@ -0,0 +1,84 @@ +{{template "head.html" .}} +{{template "nav.html" .}} + +<div class="page-wrap"> + <aside> + <a href="/calendar/create" class="new">New event</a> + <!-- TODO: fetch list of address books --> + <a href="#" class="active">{{.Calendar.Name}}</a> + <a href="#">Personal</a> + </aside> + + <div class="container"> + <main class="calendar"> + <section class="actions"> + {{ template "calendar-header.html" . }} + </section> + + <section class="dates"> + <h4 class="weekday sunday-top">Sunday</h4> + <h4 class="weekday monday-top">Monday</h4> + <h4 class="weekday tuesday-top">Tuesday</h4> + <h4 class="weekday wednesday-top">Wednesday</h4> + <h4 class="weekday thursday-top">Thursday</h4> + <h4 class="weekday friday-top">Friday</h4> + <h4 class="weekday saturday-top">Saturday</h4> + + {{$base := .}} + {{range .Dates}} + <div class="date + {{if ne $base.Time.Month .Month}}extra{{end}} + {{if and (eq $base.Now.Month .Month) (eq $base.Now.Day .Day)}}active{{end}} + "> + {{if eq $base.Time.Month .Month}} + <a href="#" class="date-link"></a> + {{end}} + <div class="events"> + {{$events := (call $base.EventsForDate .)}} + {{if $events}} + <ul> + {{$overflow := 0}} + {{if gt (len $events) 3}} + {{$overflow = call $base.Sub (len $events) 3}} + {{$events = slice $events 0 3}} + {{end}} + + {{range $events}} + {{$event := index .Data.Events 0}} + <li> + <span class="start-time"> + {{($event.DateTimeStart nil).Format "15:04"}} + </span> + {{$event.Props.Text "SUMMARY"}} + </li> + {{end}} + {{if ne $overflow 0}} + <li class="overflow">...and {{$overflow}} more</li> + {{end}} + </ul> + {{end}} + </div> + <h4> + <span class="mo">{{.Format "Jan"}}</span> + <span class="da">{{.Format "2"}}{{call $base.DaySuffix .Day}}</span> + </h4> + </div> + {{end}} + + <h4 class="weekday sunday-bottom">Sunday</h4> + <h4 class="weekday monday-bottom">Monday</h4> + <h4 class="weekday tuesday-bottom">Tuesday</h4> + <h4 class="weekday wednesday-bottom">Wednesday</h4> + <h4 class="weekday thursday-bottom">Thursday</h4> + <h4 class="weekday friday-bottom">Friday</h4> + <h4 class="weekday saturday-bottom">Saturday</h4> + </section> + + <section class="actions"> + {{ template "calendar-header.html" . }} + </section> + </main> + </div> +</div> + +{{template "foot.html"}} |