mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	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>  </details> After: 
This commit is contained in:
		@@ -19,9 +19,8 @@
 | 
			
		||||
			{{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>
 | 
			
		||||
			{{end}}
 | 
			
		||||
			<div class="ui language bottom floating slide up dropdown link item">
 | 
			
		||||
				{{svg "octicon-globe"}}
 | 
			
		||||
				<span>{{.locale.LangName}}</span>
 | 
			
		||||
			<div class="ui dropdown upward language">
 | 
			
		||||
				<span>{{svg "octicon-globe"}} {{.locale.LangName}}</span>
 | 
			
		||||
				<div class="menu language-menu">
 | 
			
		||||
					{{range .AllLangs}}
 | 
			
		||||
						<a lang="{{.Lang}}" data-url="{{AppSubUrl}}/?lang={{.Lang}}" class="item {{if eq $.locale.Lang .Lang}}active selected{{end}}">{{.Name}}</a>
 | 
			
		||||
 
 | 
			
		||||
@@ -28,34 +28,29 @@
 | 
			
		||||
				</div>
 | 
			
		||||
			{{end}}
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="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}}
 | 
			
		||||
		{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<a id="manage_topic" class="muted">{{.locale.Tr "repo.topic.manage_topics"}}</a>{{end}}
 | 
			
		||||
		<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}}
 | 
			
		||||
			{{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>
 | 
			
		||||
		{{end}}
 | 
			
		||||
		{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}
 | 
			
		||||
		<div class="ui repo-topic-edit grid form gt-hidden" id="topic_edit">
 | 
			
		||||
			<div class="fourteen wide column">
 | 
			
		||||
				<div class="field">
 | 
			
		||||
					<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}}">
 | 
			
		||||
						{{range .Topics}}
 | 
			
		||||
						<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>
 | 
			
		||||
						{{end}}
 | 
			
		||||
						<div class="text"></div>
 | 
			
		||||
					</div>
 | 
			
		||||
		<div class="ui form gt-hidden gt-df gt-mt-4" id="topic_edit">
 | 
			
		||||
			<div class="field gt-f1 gt-mr-3">
 | 
			
		||||
				<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"}}">
 | 
			
		||||
					<input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if lt (Add $i 1) (len $.Topics)}},{{end}}{{end}}">
 | 
			
		||||
					{{range .Topics}}
 | 
			
		||||
						{{/* keey the same layout as Fomantic UI generated labels */}}
 | 
			
		||||
						<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}}
 | 
			
		||||
					<div class="text"></div>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="two wide column">
 | 
			
		||||
				<a class="ui button primary" role="button" tabindex="0" id="save_topic"
 | 
			
		||||
				data-link="{{.RepoLink}}/topics">{{.locale.Tr "repo.topic.done"}}</a>
 | 
			
		||||
			<div>
 | 
			
		||||
				<button class="ui basic button secondary" id="cancel_topic_edit">{{.locale.Tr "cancel"}}</button>
 | 
			
		||||
				<button class="ui primary button" id="save_topic" data-link="{{.RepoLink}}/topics">{{.locale.Tr "save"}}</button>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
		{{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}}
 | 
			
		||||
			<div class="ui warning message">
 | 
			
		||||
				{{.locale.Tr "repo.archive.title"}}
 | 
			
		||||
 
 | 
			
		||||
@@ -1854,15 +1854,15 @@ footer .container .links > *:first-child {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
footer .ui.language .menu {
 | 
			
		||||
  max-height: 500px;
 | 
			
		||||
  height: 500px;
 | 
			
		||||
  max-height: calc(100vh - 60px);
 | 
			
		||||
  overflow-y: auto;
 | 
			
		||||
  margin-bottom: 7px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
footer .ui.language .svg {
 | 
			
		||||
  margin-right: 0.15em;
 | 
			
		||||
  vertical-align: top;
 | 
			
		||||
  margin-top: calc(2em - 16px);
 | 
			
		||||
  margin-top: 1px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
footer .ui.left,
 | 
			
		||||
@@ -2387,6 +2387,10 @@ a.ui.label:hover {
 | 
			
		||||
  color: var(--color-text);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ui.tertiary.button:focus {
 | 
			
		||||
  color: var(--color-text-dark);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ui.primary.label,
 | 
			
		||||
.ui.primary.labels .label {
 | 
			
		||||
  background-color: var(--color-primary) !important;
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@
 | 
			
		||||
.gt-pointer-events-none { pointer-events: none !important; }
 | 
			
		||||
.gt-relative { position: relative !important; }
 | 
			
		||||
.gt-overflow-x-scroll { overflow-x: scroll !important; }
 | 
			
		||||
.gt-cursor-default { cursor: default !important; }
 | 
			
		||||
 | 
			
		||||
.gt-mono {
 | 
			
		||||
  font-family: var(--fonts-monospace) !important;
 | 
			
		||||
 
 | 
			
		||||
@@ -3062,21 +3062,10 @@ tbody.commit-list {
 | 
			
		||||
  top: -2px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#topic_edit {
 | 
			
		||||
  margin-top: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#repo-topics {
 | 
			
		||||
  margin-top: 5px;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  flex-wrap: wrap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.repo-topic {
 | 
			
		||||
  font-weight: normal !important;
 | 
			
		||||
#repo-topics .repo-topic {
 | 
			
		||||
  font-weight: normal;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  margin: 2px !important;
 | 
			
		||||
  margin: 2px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#new-dependency-drop-list.ui.selection.dropdown {
 | 
			
		||||
@@ -3092,18 +3081,6 @@ tbody.commit-list {
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#manage_topic {
 | 
			
		||||
  font-size: 12px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.label + #manage_topic {
 | 
			
		||||
  margin-left: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ui.small.label.topic {
 | 
			
		||||
  margin-bottom: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.repo-header {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <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 === 'organizations'}" @click="changeTab('organizations')">{{ textOrganization }}</a>
 | 
			
		||||
    </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -83,7 +83,7 @@ export function initGlobalCommon() {
 | 
			
		||||
  const $uiDropdowns = $('.ui.dropdown');
 | 
			
		||||
 | 
			
		||||
  // 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,
 | 
			
		||||
  // clicking an item will jump to somewhere else or trigger an action/function.
 | 
			
		||||
@@ -111,14 +111,12 @@ export function initGlobalCommon() {
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // special animations/popup-directions
 | 
			
		||||
  $uiDropdowns.filter('.slide.up').dropdown({transition: 'slide up'});
 | 
			
		||||
  $uiDropdowns.filter('.upward').dropdown({direction: 'upward'});
 | 
			
		||||
  // special popup-directions
 | 
			
		||||
  $uiDropdowns.filter('.upward').dropdown('setting', 'direction', 'upward');
 | 
			
		||||
 | 
			
		||||
  $('.ui.checkbox').checkbox();
 | 
			
		||||
 | 
			
		||||
  $('.tabular.menu .item').tab();
 | 
			
		||||
  $('.tabable.menu .item').tab();
 | 
			
		||||
 | 
			
		||||
  $('.toggle.button').on('click', function () {
 | 
			
		||||
    toggleElem($($(this).data('target')));
 | 
			
		||||
 
 | 
			
		||||
@@ -6,27 +6,29 @@ const {appSubUrl, csrfToken} = window.config;
 | 
			
		||||
 | 
			
		||||
export function initRepoTopicBar() {
 | 
			
		||||
  const mgrBtn = $('#manage_topic');
 | 
			
		||||
  if (!mgrBtn.length) return;
 | 
			
		||||
  const editDiv = $('#topic_edit');
 | 
			
		||||
  const viewDiv = $('#repo-topics');
 | 
			
		||||
  const saveBtn = $('#save_topic');
 | 
			
		||||
  const topicDropdown = $('#topic_edit .dropdown');
 | 
			
		||||
  const topicForm = $('#topic_edit.ui.form');
 | 
			
		||||
  const topicPrompts = getPrompts();
 | 
			
		||||
  const topicForm = editDiv; // the old logic, editDiv is topicForm
 | 
			
		||||
  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', () => {
 | 
			
		||||
    hideElem(viewDiv);
 | 
			
		||||
    showElem(editDiv);
 | 
			
		||||
    topicDropdownSearch.focus();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  function getPrompts() {
 | 
			
		||||
    const hidePrompt = $('#validate_prompt');
 | 
			
		||||
    const prompts = {
 | 
			
		||||
      countPrompt: hidePrompt.children('#count_prompt').text(),
 | 
			
		||||
      formatPrompt: hidePrompt.children('#format_prompt').text()
 | 
			
		||||
    };
 | 
			
		||||
    hidePrompt.remove();
 | 
			
		||||
    return prompts;
 | 
			
		||||
  }
 | 
			
		||||
  $('#cancel_topic_edit').on('click', () => {
 | 
			
		||||
    hideElem(editDiv);
 | 
			
		||||
    showElem(viewDiv);
 | 
			
		||||
    mgrBtn.focus();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  saveBtn.on('click', () => {
 | 
			
		||||
    const topics = $('input[name=topics]').val();
 | 
			
		||||
@@ -39,13 +41,11 @@ export function initRepoTopicBar() {
 | 
			
		||||
        viewDiv.children('.topic').remove();
 | 
			
		||||
        if (topics.length) {
 | 
			
		||||
          const topicArray = topics.split(',');
 | 
			
		||||
 | 
			
		||||
          const last = viewDiv.children('a').last();
 | 
			
		||||
          for (let i = 0; i < topicArray.length; i++) {
 | 
			
		||||
            const link = $('<a class="ui repo-topic large label topic"></a>');
 | 
			
		||||
            link.attr('href', `${appSubUrl}/explore/repos?q=${encodeURIComponent(topicArray[i])}&topic=1`);
 | 
			
		||||
            link.text(topicArray[i]);
 | 
			
		||||
            link.insertBefore(last);
 | 
			
		||||
            link.insertBefore(mgrBtn); // insert all new topics before manage button
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        hideElem(editDiv);
 | 
			
		||||
@@ -86,9 +86,6 @@ export function initRepoTopicBar() {
 | 
			
		||||
      duration: 200,
 | 
			
		||||
      variation: false,
 | 
			
		||||
    },
 | 
			
		||||
    className: {
 | 
			
		||||
      label: 'ui small label'
 | 
			
		||||
    },
 | 
			
		||||
    apiSettings: {
 | 
			
		||||
      url: `${appSubUrl}/explore/topics/search?q={query}`,
 | 
			
		||||
      throttle: 500,
 | 
			
		||||
@@ -101,7 +98,7 @@ export function initRepoTopicBar() {
 | 
			
		||||
        const query = stripTags(this.urlData.query.trim());
 | 
			
		||||
        let found_query = false;
 | 
			
		||||
        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'));
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -88,21 +88,15 @@ import {initFormattingReplacements} from './features/formatting.js';
 | 
			
		||||
import {initCopyContent} from './features/copycontent.js';
 | 
			
		||||
import {initCaptcha} from './features/captcha.js';
 | 
			
		||||
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 {initGiteaFomantic} from './modules/fomantic.js';
 | 
			
		||||
 | 
			
		||||
// 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.
 | 
			
		||||
// TODO: replace them with CustomElements
 | 
			
		||||
initFormattingReplacements();
 | 
			
		||||
 | 
			
		||||
// 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;
 | 
			
		||||
// 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();
 | 
			
		||||
// Init Gitea's Fomantic settings
 | 
			
		||||
initGiteaFomantic();
 | 
			
		||||
 | 
			
		||||
$(document).ready(() => {
 | 
			
		||||
  initGlobalCommon();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								web_src/js/modules/fomantic.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								web_src/js/modules/fomantic.js
									
									
									
									
									
										Normal 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();
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user