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

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  , 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)
pull/23676/head^2
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();
}