Fine tune more downdrop settings, use SVG for labels, improve Repo Topic Edit form (#23626)

Although it seems that some different purposes are mixed in this PR,
however, they are all related, and can be tested together, so I put them
together to save everyone's time.

Diff: `+79 −84`, everything becomes much better.

### Improve the dropdown settings.

Move all fomantic-init related code into our `fomantic.js`

Fine-tune some dropdown global settings, see the comments.

Also help to fix the first problem in #23625 , cc: @yp05327 

The "language" menu has been simplified, and it works with small-height
window better.

### Use SVG instead of `<i class="delete icon">`

It's also done by `$.fn.dropdown.settings.templates.label` , cc:
@silverwind

### Remove incorrect `tabable` CSS class

It doesn't have CSS styles, and it was only in Vue. So it's totally
unnecessary, remove it by the way.

### Improve the Repo Topic Edit form

* Simplify the code
* Add a "Cancel" button
* Align elements

Before:

<details>

![image](https://user-images.githubusercontent.com/2114189/223325782-f09532de-0c38-4742-ba86-ed35cc9a858d.png)

</details>

After:

![image](https://user-images.githubusercontent.com/2114189/226796347-207feb0a-b3cd-4820-8a3e-01930bab1069.png)
This commit is contained in:
wxiaoguang 2023-03-26 19:31:26 +08:00 committed by GitHub
parent ac64c82974
commit 12fff36d05
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 80 additions and 86 deletions

View file

@ -19,9 +19,8 @@
{{if .ShowFooterBranding}} {{if .ShowFooterBranding}}
<a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea">{{svg "octicon-mark-github"}}<span class="sr-only">GitHub</span></a> <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea">{{svg "octicon-mark-github"}}<span class="sr-only">GitHub</span></a>
{{end}} {{end}}
<div class="ui language bottom floating slide up dropdown link item"> <div class="ui dropdown upward language">
{{svg "octicon-globe"}} <span>{{svg "octicon-globe"}} {{.locale.LangName}}</span>
<span>{{.locale.LangName}}</span>
<div class="menu language-menu"> <div class="menu language-menu">
{{range .AllLangs}} {{range .AllLangs}}
<a lang="{{.Lang}}" data-url="{{AppSubUrl}}/?lang={{.Lang}}" class="item {{if eq $.locale.Lang .Lang}}active selected{{end}}">{{.Name}}</a> <a lang="{{.Lang}}" data-url="{{AppSubUrl}}/?lang={{.Lang}}" class="item {{if eq $.locale.Lang .Lang}}active selected{{end}}">{{.Name}}</a>

View file

@ -28,34 +28,29 @@
</div> </div>
{{end}} {{end}}
</div> </div>
<div class="gt-mt-3" id="repo-topics"> <div class="gt-df gt-ac gt-fw gt-mt-3" id="repo-topics">
{{range .Topics}}<a class="ui repo-topic large label topic" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}} {{range .Topics}}<a class="ui repo-topic large label topic" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<a id="manage_topic" class="muted">{{.locale.Tr "repo.topic.manage_topics"}}</a>{{end}} {{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<button id="manage_topic" class="ui button tiny tertiary gt-ml-2">{{.locale.Tr "repo.topic.manage_topics"}}</button>{{end}}
</div> </div>
{{end}} {{end}}
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}} {{if and .Permission.IsAdmin (not .Repository.IsArchived)}}
<div class="ui repo-topic-edit grid form gt-hidden" id="topic_edit"> <div class="ui form gt-hidden gt-df gt-mt-4" id="topic_edit">
<div class="fourteen wide column"> <div class="field gt-f1 gt-mr-3">
<div class="field"> <div class="ui fluid multiple search selection dropdown" data-text-count-prompt="{{.locale.Tr "repo.topic.count_prompt"}}" data-text-format-prompt="{{.locale.Tr "repo.topic.format_prompt"}}">
<div class="ui fluid multiple search selection dropdown"> <input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if lt (Add $i 1) (len $.Topics)}},{{end}}{{end}}">
<input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if lt (Add $i 1) (len $.Topics)}},{{end}}{{end}}"> {{range .Topics}}
{{range .Topics}} {{/* keey the same layout as Fomantic UI generated labels */}}
<div class="ui small label topic transition visible" data-value="{{.Name}}" style="display: inline-block !important; cursor: default;">{{.Name}}{{svg "octicon-x" 16 "delete icon gt-ml-3 gt-mt-1"}}</div> <a class="ui label transition visible gt-cursor-default" data-value="{{.Name}}" style="display: inline-block !important;">{{.Name}}{{svg "octicon-x" 16 "delete icon"}}</a>
{{end}} {{end}}
<div class="text"></div> <div class="text"></div>
</div>
</div> </div>
</div> </div>
<div class="two wide column"> <div>
<a class="ui button primary" role="button" tabindex="0" id="save_topic" <button class="ui basic button secondary" id="cancel_topic_edit">{{.locale.Tr "cancel"}}</button>
data-link="{{.RepoLink}}/topics">{{.locale.Tr "repo.topic.done"}}</a> <button class="ui primary button" id="save_topic" data-link="{{.RepoLink}}/topics">{{.locale.Tr "save"}}</button>
</div> </div>
</div> </div>
{{end}} {{end}}
<div class="gt-hidden" id="validate_prompt">
<span id="count_prompt">{{.locale.Tr "repo.topic.count_prompt"}}</span>
<span id="format_prompt">{{.locale.Tr "repo.topic.format_prompt"}}</span>
</div>
{{if .Repository.IsArchived}} {{if .Repository.IsArchived}}
<div class="ui warning message"> <div class="ui warning message">
{{.locale.Tr "repo.archive.title"}} {{.locale.Tr "repo.archive.title"}}

View file

@ -1854,15 +1854,15 @@ footer .container .links > *:first-child {
} }
footer .ui.language .menu { footer .ui.language .menu {
max-height: 500px; height: 500px;
max-height: calc(100vh - 60px);
overflow-y: auto; overflow-y: auto;
margin-bottom: 7px; margin-bottom: 7px;
} }
footer .ui.language .svg { footer .ui.language .svg {
margin-right: 0.15em; margin-right: 0.15em;
vertical-align: top; margin-top: 1px;
margin-top: calc(2em - 16px);
} }
footer .ui.left, footer .ui.left,
@ -2387,6 +2387,10 @@ a.ui.label:hover {
color: var(--color-text); color: var(--color-text);
} }
.ui.tertiary.button:focus {
color: var(--color-text-dark);
}
.ui.primary.label, .ui.primary.label,
.ui.primary.labels .label { .ui.primary.labels .label {
background-color: var(--color-primary) !important; background-color: var(--color-primary) !important;

View file

@ -23,6 +23,7 @@
.gt-pointer-events-none { pointer-events: none !important; } .gt-pointer-events-none { pointer-events: none !important; }
.gt-relative { position: relative !important; } .gt-relative { position: relative !important; }
.gt-overflow-x-scroll { overflow-x: scroll !important; } .gt-overflow-x-scroll { overflow-x: scroll !important; }
.gt-cursor-default { cursor: default !important; }
.gt-mono { .gt-mono {
font-family: var(--fonts-monospace) !important; font-family: var(--fonts-monospace) !important;

View file

@ -3062,21 +3062,10 @@ tbody.commit-list {
top: -2px; top: -2px;
} }
#topic_edit { #repo-topics .repo-topic {
margin-top: 5px; font-weight: normal;
}
#repo-topics {
margin-top: 5px;
display: flex;
align-items: center;
flex-wrap: wrap;
}
.repo-topic {
font-weight: normal !important;
cursor: pointer; cursor: pointer;
margin: 2px !important; margin: 2px;
} }
#new-dependency-drop-list.ui.selection.dropdown { #new-dependency-drop-list.ui.selection.dropdown {
@ -3092,18 +3081,6 @@ tbody.commit-list {
overflow: hidden; overflow: hidden;
} }
#manage_topic {
font-size: 12px;
}
.label + #manage_topic {
margin-left: 5px;
}
.ui.small.label.topic {
margin-bottom: 4px;
}
.repo-header { .repo-header {
display: flex; display: flex;
align-items: center; align-items: center;

View file

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<div v-if="!isOrganization" class="ui two item tabable menu"> <div v-if="!isOrganization" class="ui two item menu">
<a :class="{item: true, active: tab === 'repos'}" @click="changeTab('repos')">{{ textRepository }}</a> <a :class="{item: true, active: tab === 'repos'}" @click="changeTab('repos')">{{ textRepository }}</a>
<a :class="{item: true, active: tab === 'organizations'}" @click="changeTab('organizations')">{{ textOrganization }}</a> <a :class="{item: true, active: tab === 'organizations'}" @click="changeTab('organizations')">{{ textOrganization }}</a>
</div> </div>

View file

@ -83,7 +83,7 @@ export function initGlobalCommon() {
const $uiDropdowns = $('.ui.dropdown'); const $uiDropdowns = $('.ui.dropdown');
// do not init "custom" dropdowns, "custom" dropdowns are managed by their own code. // do not init "custom" dropdowns, "custom" dropdowns are managed by their own code.
$uiDropdowns.filter(':not(.custom)').dropdown({fullTextSearch: 'exact'}); $uiDropdowns.filter(':not(.custom)').dropdown();
// The "jump" means this dropdown is mainly used for "menu" purpose, // The "jump" means this dropdown is mainly used for "menu" purpose,
// clicking an item will jump to somewhere else or trigger an action/function. // clicking an item will jump to somewhere else or trigger an action/function.
@ -111,14 +111,12 @@ export function initGlobalCommon() {
}, },
}); });
// special animations/popup-directions // special popup-directions
$uiDropdowns.filter('.slide.up').dropdown({transition: 'slide up'}); $uiDropdowns.filter('.upward').dropdown('setting', 'direction', 'upward');
$uiDropdowns.filter('.upward').dropdown({direction: 'upward'});
$('.ui.checkbox').checkbox(); $('.ui.checkbox').checkbox();
$('.tabular.menu .item').tab(); $('.tabular.menu .item').tab();
$('.tabable.menu .item').tab();
$('.toggle.button').on('click', function () { $('.toggle.button').on('click', function () {
toggleElem($($(this).data('target'))); toggleElem($($(this).data('target')));

View file

@ -6,27 +6,29 @@ const {appSubUrl, csrfToken} = window.config;
export function initRepoTopicBar() { export function initRepoTopicBar() {
const mgrBtn = $('#manage_topic'); const mgrBtn = $('#manage_topic');
if (!mgrBtn.length) return;
const editDiv = $('#topic_edit'); const editDiv = $('#topic_edit');
const viewDiv = $('#repo-topics'); const viewDiv = $('#repo-topics');
const saveBtn = $('#save_topic'); const saveBtn = $('#save_topic');
const topicDropdown = $('#topic_edit .dropdown'); const topicDropdown = $('#topic_edit .dropdown');
const topicForm = $('#topic_edit.ui.form'); const topicForm = editDiv; // the old logic, editDiv is topicForm
const topicPrompts = getPrompts(); const topicDropdownSearch = topicDropdown.find('input.search');
const topicPrompts = {
countPrompt: topicDropdown.attr('data-text-count-prompt'),
formatPrompt: topicDropdown.attr('data-text-format-prompt'),
};
mgrBtn.on('click', () => { mgrBtn.on('click', () => {
hideElem(viewDiv); hideElem(viewDiv);
showElem(editDiv); showElem(editDiv);
topicDropdownSearch.focus();
}); });
function getPrompts() { $('#cancel_topic_edit').on('click', () => {
const hidePrompt = $('#validate_prompt'); hideElem(editDiv);
const prompts = { showElem(viewDiv);
countPrompt: hidePrompt.children('#count_prompt').text(), mgrBtn.focus();
formatPrompt: hidePrompt.children('#format_prompt').text() });
};
hidePrompt.remove();
return prompts;
}
saveBtn.on('click', () => { saveBtn.on('click', () => {
const topics = $('input[name=topics]').val(); const topics = $('input[name=topics]').val();
@ -39,13 +41,11 @@ export function initRepoTopicBar() {
viewDiv.children('.topic').remove(); viewDiv.children('.topic').remove();
if (topics.length) { if (topics.length) {
const topicArray = topics.split(','); const topicArray = topics.split(',');
const last = viewDiv.children('a').last();
for (let i = 0; i < topicArray.length; i++) { for (let i = 0; i < topicArray.length; i++) {
const link = $('<a class="ui repo-topic large label topic"></a>'); const link = $('<a class="ui repo-topic large label topic"></a>');
link.attr('href', `${appSubUrl}/explore/repos?q=${encodeURIComponent(topicArray[i])}&topic=1`); link.attr('href', `${appSubUrl}/explore/repos?q=${encodeURIComponent(topicArray[i])}&topic=1`);
link.text(topicArray[i]); link.text(topicArray[i]);
link.insertBefore(last); link.insertBefore(mgrBtn); // insert all new topics before manage button
} }
} }
hideElem(editDiv); hideElem(editDiv);
@ -86,9 +86,6 @@ export function initRepoTopicBar() {
duration: 200, duration: 200,
variation: false, variation: false,
}, },
className: {
label: 'ui small label'
},
apiSettings: { apiSettings: {
url: `${appSubUrl}/explore/topics/search?q={query}`, url: `${appSubUrl}/explore/topics/search?q={query}`,
throttle: 500, throttle: 500,
@ -101,7 +98,7 @@ export function initRepoTopicBar() {
const query = stripTags(this.urlData.query.trim()); const query = stripTags(this.urlData.query.trim());
let found_query = false; let found_query = false;
const current_topics = []; const current_topics = [];
topicDropdown.find('div.label.visible.topic,a.label.visible').each((_, el) => { topicDropdown.find('a.label.visible').each((_, el) => {
current_topics.push(el.getAttribute('data-value')); current_topics.push(el.getAttribute('data-value'));
}); });

View file

@ -88,21 +88,15 @@ import {initFormattingReplacements} from './features/formatting.js';
import {initCopyContent} from './features/copycontent.js'; import {initCopyContent} from './features/copycontent.js';
import {initCaptcha} from './features/captcha.js'; import {initCaptcha} from './features/captcha.js';
import {initRepositoryActionView} from './components/RepoActionView.vue'; import {initRepositoryActionView} from './components/RepoActionView.vue';
import {initAriaCheckboxPatch} from './modules/aria/checkbox.js';
import {initAriaDropdownPatch} from './modules/aria/dropdown.js';
import {initGlobalTooltips} from './modules/tippy.js'; import {initGlobalTooltips} from './modules/tippy.js';
import {initGiteaFomantic} from './modules/fomantic.js';
// Run time-critical code as soon as possible. This is safe to do because this // Run time-critical code as soon as possible. This is safe to do because this
// script appears at the end of <body> and rendered HTML is accessible at that point. // script appears at the end of <body> and rendered HTML is accessible at that point.
// TODO: replace them with CustomElements
initFormattingReplacements(); initFormattingReplacements();
// Init Gitea's Fomantic settings
// Silence fomantic's error logging when tabs are used without a target content element initGiteaFomantic();
$.fn.tab.settings.silent = true;
// Disable the behavior of fomantic to toggle the checkbox when you press enter on a checkbox element.
$.fn.checkbox.settings.enableEnterKey = false;
// Use the patches to improve accessibility, these patches are designed to be as independent as possible, make it easy to modify or remove in the future.
initAriaCheckboxPatch();
initAriaDropdownPatch();
$(document).ready(() => { $(document).ready(() => {
initGlobalCommon(); initGlobalCommon();

View file

@ -0,0 +1,29 @@
import $ from 'jquery';
import {initAriaCheckboxPatch} from './aria/checkbox.js';
import {initAriaDropdownPatch} from './aria/dropdown.js';
import {svg} from '../svg.js';
export function initGiteaFomantic() {
// Silence fomantic's error logging when tabs are used without a target content element
$.fn.tab.settings.silent = true;
// Disable the behavior of fomantic to toggle the checkbox when you press enter on a checkbox element.
$.fn.checkbox.settings.enableEnterKey = false;
// Prevent Fomantic from guessing the popup direction.
// Otherwise, if the viewport height is small, Fomantic would show the popup upward,
// if the dropdown is at the beginning of the page, then the top part would be clipped by the window view, eg: Issue List "Sort" dropdown
$.fn.dropdown.settings.direction = 'downward';
// By default, use "exact match" for full text search
$.fn.dropdown.settings.fullTextSearch = 'exact';
// Do not use "cursor: pointer" for dropdown labels
$.fn.dropdown.settings.className.label += ' gt-cursor-default';
// Always use Gitea's SVG icons
$.fn.dropdown.settings.templates.label = function(_value, text, preserveHTML, className) {
const escape = $.fn.dropdown.settings.templates.escape;
return escape(text, preserveHTML) + svg('octicon-x', 16, `${className.delete} icon`);
};
// Use the patches to improve accessibility, these patches are designed to be as independent as possible, make it easy to modify or remove in the future.
initAriaCheckboxPatch();
initAriaDropdownPatch();
}