Skip to content

Commit 662db4a

Browse files
authored
Improve tags list page (#34898)
1 parent 95964dd commit 662db4a

File tree

4 files changed

+90
-76
lines changed

4 files changed

+90
-76
lines changed

templates/repo/tag/list.tmpl

Lines changed: 39 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,63 +5,57 @@
55
{{template "base/alert" .}}
66
{{template "repo/release_tag_header" .}}
77
<h4 class="ui top attached header">
8-
<div class="five wide column tw-flex tw-items-center">
9-
{{.TagCount}} {{ctx.Locale.Tr "repo.release.tags"}}
10-
</div>
8+
{{.TagCount}} {{ctx.Locale.Tr "repo.release.tags"}}
119
</h4>
1210
{{$canReadReleases := $.Permission.CanRead ctx.Consts.RepoUnitTypeReleases}}
1311
<div class="ui attached segment">
1412
<form class="ignore-dirty" method="get">
1513
{{template "shared/search/combo" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.tag_kind") "Tooltip" (ctx.Locale.Tr "search.tag_tooltip")}}
1614
</form>
1715
</div>
18-
<div class="ui attached table segment">
16+
<div class="ui attached segment tw-p-0">
1917
{{if .Releases}}
20-
<table class="ui very basic striped fixed table single line" id="tags-table">
21-
<tbody class="tag-list">
22-
{{range $idx, $release := .Releases}}
23-
<tr>
24-
<td class="tag-list-row">
25-
<h3 class="tag-list-row-title tw-mb-2">
26-
{{if $canReadReleases}}
27-
<a class="tag-list-row-link tw-flex tw-items-center" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
28-
{{else}}
29-
<a class="tag-list-row-link tw-flex tw-items-center" href="{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
30-
{{end}}
31-
</h3>
32-
<div class="download tw-flex tw-items-center">
33-
{{if $.Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
34-
{{if .CreatedUnix}}
35-
<span class="tw-mr-2">{{svg "octicon-clock" 16 "tw-mr-1"}}{{DateUtils.TimeSince .CreatedUnix}}</span>
36-
{{end}}
18+
<div class="ui divided list" id="tags-table">
19+
{{range $idx, $release := .Releases}}
20+
<div class="item tag-list-row tw-p-4">
21+
<h3 class="tag-list-row-title tw-mb-2">
22+
{{if $canReadReleases}}
23+
<a class="tag-list-row-link" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
24+
{{else}}
25+
<a class="tag-list-row-link" href="{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}" rel="nofollow">{{.TagName}}</a>
26+
{{end}}
27+
</h3>
28+
<div class="flex-text-block muted-links tw-gap-4 tw-flex-wrap">
29+
{{if $.Permission.CanRead ctx.Consts.RepoUnitTypeCode}}
30+
{{if .CreatedUnix}}
31+
<span class="flex-text-inline">{{svg "octicon-clock"}}{{DateUtils.TimeSince .CreatedUnix}}</span>
32+
{{end}}
3733

38-
<a class="tw-mr-2 tw-font-mono muted" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "tw-mr-1"}}{{ShortSha .Sha1}}</a>
34+
<a class="flex-text-inline tw-font-mono" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit"}}{{ShortSha .Sha1}}</a>
3935

40-
{{if not $.DisableDownloadSourceArchives}}
41-
<a class="archive-link tw-mr-2 muted" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.zip" rel="nofollow">{{svg "octicon-file-zip" 16 "tw-mr-1"}}ZIP</a>
42-
<a class="archive-link tw-mr-2 muted" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip" 16 "tw-mr-1"}}TAR.GZ</a>
43-
{{end}}
36+
{{if not $.DisableDownloadSourceArchives}}
37+
<a class="archive-link flex-text-inline" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.zip" rel="nofollow">{{svg "octicon-file-zip"}}ZIP</a>
38+
<a class="archive-link flex-text-inline" href="{{$.RepoLink}}/archive/{{.TagName | PathEscapeSegments}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip"}}TAR.GZ</a>
39+
{{end}}
4440

45-
{{if (and $canReadReleases $.CanCreateRelease $release.IsTag)}}
46-
<a class="tw-mr-2 muted" href="{{$.RepoLink}}/releases/new?tag={{.TagName}}">{{svg "octicon-tag" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.new_release"}}</a>
47-
{{end}}
41+
{{if (and $canReadReleases $.CanCreateRelease $release.IsTag)}}
42+
<a class="flex-text-inline" href="{{$.RepoLink}}/releases/new?tag={{.TagName}}">{{svg "octicon-tag"}}{{ctx.Locale.Tr "repo.release.new_release"}}</a>
43+
{{end}}
4844

49-
{{if (and ($.Permission.CanWrite ctx.Consts.RepoUnitTypeCode) $release.IsTag)}}
50-
<a class="ui delete-button tw-mr-2 muted" data-url="{{$.RepoLink}}/tags/delete" data-id="{{.ID}}">
51-
{{svg "octicon-trash" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.delete_tag"}}
52-
</a>
53-
{{end}}
45+
{{if (and ($.Permission.CanWrite ctx.Consts.RepoUnitTypeCode) $release.IsTag)}}
46+
<a class="flex-text-inline link-action" data-url="{{$.RepoLink}}/tags/delete?id={{.ID}}" data-modal-confirm="#confirm-delete-tag-modal">
47+
{{svg "octicon-trash"}}{{ctx.Locale.Tr "repo.release.delete_tag"}}
48+
</a>
49+
{{end}}
5450

55-
{{if and $canReadReleases (not $release.IsTag)}}
56-
<a class="tw-mr-2 muted" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}">{{svg "octicon-tag" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.release.detail"}}</a>
57-
{{end}}
58-
{{end}}
59-
</div>
60-
</td>
61-
</tr>
62-
{{end}}
63-
</tbody>
64-
</table>
51+
{{if and $canReadReleases (not $release.IsTag)}}
52+
<a class="flex-text-inline" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}">{{svg "octicon-tag"}}{{ctx.Locale.Tr "repo.release.detail"}}</a>
53+
{{end}}
54+
{{end}}
55+
</div>
56+
</div>
57+
{{end}}
58+
</div>
6559
{{else}}
6660
{{if .NumTags}}
6761
<p class="tw-p-4">{{ctx.Locale.Tr "no_results_found"}}</p>
@@ -73,9 +67,8 @@
7367
</div>
7468

7569
{{if $.Permission.CanWrite ctx.Consts.RepoUnitTypeCode}}
76-
<div class="ui g-modal-confirm delete modal">
70+
<div id="confirm-delete-tag-modal" class="ui small modal">
7771
<div class="header">
78-
{{svg "octicon-trash"}}
7972
{{ctx.Locale.Tr "repo.release.delete_tag"}}
8073
</div>
8174
<div class="content">

web_src/css/repo/release-tag.css

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,6 @@
9191
border-bottom: none;
9292
}
9393

94-
#tags-table .tag-list-row {
95-
padding: 8px 12px;
96-
}
97-
9894
#tags-table .tag-list-row-title {
9995
font-size: 18px;
10096
font-weight: var(--font-weight-normal);

web_src/js/features/common-fetch-action.ts

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {request} from '../modules/fetch.ts';
22
import {hideToastsAll, showErrorToast} from '../modules/toast.ts';
3-
import {addDelegatedEventListener, submitEventSubmitter} from '../utils/dom.ts';
4-
import {confirmModal} from './comp/ConfirmModal.ts';
3+
import {addDelegatedEventListener, createElementFromHTML, submitEventSubmitter} from '../utils/dom.ts';
4+
import {confirmModal, createConfirmModal} from './comp/ConfirmModal.ts';
55
import type {RequestOpts} from '../types.ts';
66
import {ignoreAreYouSure} from '../vendor/jquery.are-you-sure.ts';
77

@@ -111,28 +111,44 @@ export async function submitFormFetchAction(formEl: HTMLFormElement, formSubmitt
111111
async function onLinkActionClick(el: HTMLElement, e: Event) {
112112
// A "link-action" can post AJAX request to its "data-url"
113113
// Then the browser is redirected to: the "redirect" in response, or "data-redirect" attribute, or current URL by reloading.
114-
// If the "link-action" has "data-modal-confirm" attribute, a confirm modal dialog will be shown before taking action.
114+
// If the "link-action" has "data-modal-confirm" attribute, a "confirm modal dialog" will be shown before taking action.
115+
// Attribute "data-modal-confirm" can be a modal element by "#the-modal-id", or a string content for the modal dialog.
115116
e.preventDefault();
116117
const url = el.getAttribute('data-url');
117118
const doRequest = async () => {
118-
if ('disabled' in el) el.disabled = true; // el could be A or BUTTON, but A doesn't have disabled attribute
119+
if ('disabled' in el) el.disabled = true; // el could be A or BUTTON, but "A" doesn't have the "disabled" attribute
119120
await fetchActionDoRequest(el, url, {method: el.getAttribute('data-link-action-method') || 'POST'});
120121
if ('disabled' in el) el.disabled = false;
121122
};
122123

123-
const modalConfirmContent = el.getAttribute('data-modal-confirm') ||
124-
el.getAttribute('data-modal-confirm-content') || '';
125-
if (!modalConfirmContent) {
124+
let elModal: HTMLElement | null = null;
125+
const dataModalConfirm = el.getAttribute('data-modal-confirm') || '';
126+
if (dataModalConfirm.startsWith('#')) {
127+
// eslint-disable-next-line unicorn/prefer-query-selector
128+
elModal = document.getElementById(dataModalConfirm.substring(1));
129+
if (elModal) {
130+
elModal = createElementFromHTML(elModal.outerHTML);
131+
elModal.removeAttribute('id');
132+
}
133+
}
134+
if (!elModal) {
135+
const modalConfirmContent = dataModalConfirm || el.getAttribute('data-modal-confirm-content') || '';
136+
if (modalConfirmContent) {
137+
const isRisky = el.classList.contains('red') || el.classList.contains('negative');
138+
elModal = createConfirmModal({
139+
header: el.getAttribute('data-modal-confirm-header') || '',
140+
content: modalConfirmContent,
141+
confirmButtonColor: isRisky ? 'red' : 'primary',
142+
});
143+
}
144+
}
145+
146+
if (!elModal) {
126147
await doRequest();
127148
return;
128149
}
129150

130-
const isRisky = el.classList.contains('red') || el.classList.contains('negative');
131-
if (await confirmModal({
132-
header: el.getAttribute('data-modal-confirm-header') || '',
133-
content: modalConfirmContent,
134-
confirmButtonColor: isRisky ? 'red' : 'primary',
135-
})) {
151+
if (await confirmModal(elModal)) {
136152
await doRequest();
137153
}
138154
}

web_src/js/features/comp/ConfirmModal.ts

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,29 @@ import {fomanticQuery} from '../../modules/fomantic/base.ts';
55

66
const {i18n} = window.config;
77

8-
export function confirmModal({header = '', content = '', confirmButtonColor = 'primary'} = {}): Promise<boolean> {
8+
type ConfirmModalOptions = {
9+
header?: string;
10+
content?: string;
11+
confirmButtonColor?: 'primary' | 'red' | 'green' | 'blue';
12+
}
13+
14+
export function createConfirmModal({header = '', content = '', confirmButtonColor = 'primary'}:ConfirmModalOptions = {}): HTMLElement {
15+
const headerHtml = header ? `<div class="header">${htmlEscape(header)}</div>` : '';
16+
return createElementFromHTML(`
17+
<div class="ui g-modal-confirm modal">
18+
${headerHtml}
19+
<div class="content">${htmlEscape(content)}</div>
20+
<div class="actions">
21+
<button class="ui cancel button">${svg('octicon-x')} ${htmlEscape(i18n.modal_cancel)}</button>
22+
<button class="ui ${confirmButtonColor} ok button">${svg('octicon-check')} ${htmlEscape(i18n.modal_confirm)}</button>
23+
</div>
24+
</div>
25+
`);
26+
}
27+
28+
export function confirmModal(modal: HTMLElement | ConfirmModalOptions): Promise<boolean> {
29+
if (!(modal instanceof HTMLElement)) modal = createConfirmModal(modal);
930
return new Promise((resolve) => {
10-
const headerHtml = header ? `<div class="header">${htmlEscape(header)}</div>` : '';
11-
const modal = createElementFromHTML(`
12-
<div class="ui g-modal-confirm modal">
13-
${headerHtml}
14-
<div class="content">${htmlEscape(content)}</div>
15-
<div class="actions">
16-
<button class="ui cancel button">${svg('octicon-x')} ${htmlEscape(i18n.modal_cancel)}</button>
17-
<button class="ui ${confirmButtonColor} ok button">${svg('octicon-check')} ${htmlEscape(i18n.modal_confirm)}</button>
18-
</div>
19-
</div>
20-
`);
21-
document.body.append(modal);
2231
const $modal = fomanticQuery(modal);
2332
$modal.modal({
2433
onApprove() {

0 commit comments

Comments
 (0)