v0.33.0 cf26365

issues.templ RawHistoryFolder

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package web
4
5// IssueList is the list of the issues of a repository, open or closed.
6templ IssueList(l Layout, p IssueListPage) {
7	@Shell(l, issueListRail(p)) {
8		if p.Live {
9			@LiveSlot("mr-notice")
10		}
11		<nav class="since" aria-label="State">
12			<a
13				href={ templ.SafeURL(p.OpenHref) }
14				if !p.Closed {
15					aria-current="page"
16				}
17			>open</a>
18			<a
19				href={ templ.SafeURL(p.ClosedHref) }
20				if p.Closed {
21					aria-current="page"
22				}
23			>closed</a>
24		</nav>
25		<div class="filter">
26			<label for="issue-filter" class="sr">filter</label>
27			<input
28				id="issue-filter"
29				type="search"
30				data-filter=".mrs tbody tr"
31				placeholder="Filter by title, author or state…"
32				autocomplete="off"
33				hx-on:input="ermite.filter(this)"
34			/>
35		</div>
36		<h2 class="rule-h">
37			if p.Closed {
38				<span>Closed</span>
39			} else {
40				<span>Open</span>
41			}
42			<span class="count">{ itoa(len(p.Rows)) }</span>
43		</h2>
44		@issueRows(p.Rows)
45		if len(p.Rows) == 0 {
46			if p.Closed {
47				<p class="lede dim">no closed issues.</p>
48			} else {
49				<p class="lede dim">no open issues.</p>
50			}
51		}
52		if p.Older != "" || p.Newest != "" {
53			<nav class="pager" aria-label="Pages">
54				if p.Newest != "" {
55					<a class="btn" href={ templ.SafeURL(p.Newest) }>← Newest</a>
56				} else {
57					<span class="btn" aria-disabled="true">← Newest</span>
58				}
59				if p.Older != "" {
60					<a class="btn" href={ templ.SafeURL(p.Older) } rel="next">Older →</a>
61				} else {
62					<span class="btn" aria-disabled="true">Older →</span>
63				}
64			</nav>
65		}
66	}
67}
68
69templ issueRows(rows []IssueRow) {
70	if len(rows) > 0 {
71		<table class="list mrs" aria-label="Issues">
72			<tbody data-rows>
73				for _, i := range rows {
74					<tr data-cursor?={ i.Cursor } data-state={ i.State }>
75						<td class="c-n mute">{ i.Number }</td>
76						<td class="c-state">{ i.State }</td>
77						<td class="c-subject"><a href={ templ.SafeURL(i.Href) }>{ i.Title }</a></td>
78						<td class="c-who dim">{ i.Author }</td>
79						<td class="c-when nowrap dim">
80							if i.WhenAt != "" {
81								<time datetime={ i.WhenAt } title={ i.WhenAt }>{ i.When }</time>
82							}
83						</td>
84					</tr>
85				}
86			</tbody>
87		</table>
88	}
89}
90
91templ issueListRail(p IssueListPage) {
92	<section aria-labelledby="r-states">
93		<h2 id="r-states">States</h2>
94		<dl class="kv">
95			<dt>open</dt>
96			<dd>being worked on</dd>
97			<dt>closed</dt>
98			<dd>done with, can be reopened</dd>
99		</dl>
100	</section>
101	if p.NewHref != "" {
102		<section aria-labelledby="r-new">
103			<h2 id="r-new">Open one</h2>
104			<a class="btn primary" href={ templ.SafeURL(p.NewHref) }>New issue</a>
105		</section>
106	}
107}
108
109// IssueNew is the form that opens an issue.
110templ IssueNew(l Layout, p IssueNewPage) {
111	@Shell(l, issueNewRail()) {
112		<header class="show mr-h">
113			<h1>New issue</h1>
114		</header>
115		<form class="cform create" method="post" action={ templ.SafeURL(p.Action) }>
116			<label for="issue-title">title</label>
117			<input id="issue-title" name="title" type="text" maxlength="120" required autocomplete="off" autofocus/>
118			<label for="issue-body">description</label>
119			<textarea id="issue-body" name="body" rows="8" maxlength="10000" placeholder="Markdown, optional"></textarea>
120			<div class="f-btns">
121				<button type="submit" class="btn primary">Create issue</button>
122				<a class="btn" href={ templ.SafeURL(p.Cancel) }>Cancel</a>
123			</div>
124		</form>
125	}
126}
127
128templ issueNewRail() {
129	<section aria-labelledby="r-about">
130		<h2 id="r-about">Issues</h2>
131		<p class="dim">issues share their numbers with the merge requests of the repository.</p>
132	</section>
133}
134
135// IssueShow is an issue: its facts, its body, its comments and, for the
136// owner, the form to comment and the button that closes or reopens it.
137templ IssueShow(l Layout, p IssuePage) {
138	@Shell(l, issueRail(p)) {
139		<section class="mr-card">
140			<header class="show mr-h">
141				<h1><span class="mute">{ p.Number }</span> { p.Title }</h1>
142				<dl class="kv show-kv">
143					<dt>state</dt>
144					<dd class="mr-state" data-state={ p.State }>{ p.State }</dd>
145					<dt>opened</dt>
146					<dd>
147						{ p.Opened.Name }
148						if p.Opened.WhenAt != "" {
149							{ " · " }<time datetime={ p.Opened.WhenAt } title={ p.Opened.Abs }>{ p.Opened.When }</time>
150						}
151					</dd>
152					if p.ClosedBy != nil {
153						<dt>closed by</dt>
154						<dd><a class="ref" href={ templ.SafeURL(p.ClosedBy.Href) }>{ p.ClosedBy.Label }</a></dd>
155					}
156					if p.UpdatedAt != "" {
157						<dt>updated</dt>
158						<dd><time datetime={ p.UpdatedAt }>{ p.Updated }</time></dd>
159					}
160				</dl>
161			</header>
162			if p.Desc != "" {
163				<article class="mr-desc prose" aria-label="description" hx-ignore>
164					@templ.Raw(string(p.Desc))
165				</article>
166			}
167		</section>
168		if p.Live {
169			@LiveSlot("mr-notice")
170		}
171		<h2 class="rule-h" id="comments"><span>Comments</span><span class="count">{ itoa(len(p.Comments)) }</span></h2>
172		for _, c := range p.Comments {
173			@commentView(c, "")
174		}
175		if len(p.Comments) == 0 {
176			<p class="dim">no comments yet.</p>
177		}
178		if p.Form != nil {
179			@commentForm(*p.Form, "")
180		}
181	}
182}
183
184templ issueRail(p IssuePage) {
185	if len(p.References) > 0 {
186		<section aria-labelledby="r-refs">
187			<h2 id="r-refs">Merge requests</h2>
188			<ul>
189				for _, r := range p.References {
190					<li>
191						<a class="ref" href={ templ.SafeURL(r.Href) }>{ r.Number }</a>
192						{ " " + r.Title + " " }
193						<span class="mr-state" data-state={ r.State }>{ r.State }</span>
194						if r.Closes {
195							{ " · closes" }
196						}
197					</li>
198				}
199			</ul>
200		</section>
201	}
202	if p.Transition != nil {
203		<section aria-labelledby="r-act">
204			<h2 id="r-act">Issue</h2>
205			<form method="post" action={ templ.SafeURL(p.Transition.Action) }>
206				<button type="submit" class="btn">{ p.Transition.Label }</button>
207			</form>
208		</section>
209	}
210}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 45

↑↓ moveenter openesc close