Merge branch 'main' into ssh-mirror-migrations

This commit is contained in:
pomidorry authored and GitHub committed 2026-06-07 17:51:59 +03:00
commit 26b4aae364
107 files changed
+918 -783

No files matched your search

-5
View File
@@ -454,11 +454,6 @@ img.ui.avatar,
margin-right: 4px;
}
.ui.inline.delete-button {
padding: 8px 15px;
font-weight: var(--font-weight-normal);
}
.ui .migrate {
color: var(--color-text-light-2) !important;
}
-1
View File
@@ -37,7 +37,6 @@
@import "./modules/charescape.css";
@import "./shared/flex-list.css";
@import "./shared/milestone.css";
@import "./shared/settings.css";
@import "./features/dropzone.css";
+4
View File
@@ -80,6 +80,10 @@
border-top-width: 0;
}
.ui.dropdown .menu .hidden { /* for hidden items and dividers */
display: none;
}
.ui.dropdown .menu > .header {
margin: 1rem 0 0.75rem;
padding: 0 1.14285714rem;
+1 -1
View File
@@ -2,7 +2,7 @@
and material icons should have no "fill" set explicitly, otherwise some like ".editorconfig" won't render correctly */
.svg:not(.git-entry-icon) {
display: inline-block;
vertical-align: text-top;
vertical-align: middle; /* middle is the best choice for different font sizes from 1px to 36px when no flex */
fill: currentcolor;
}
+44 -8
View File
@@ -34,14 +34,6 @@
}
}
#issue-list .issue-item-title {
font-size: 16px;
font-weight: var(--font-weight-semibold);
color: var(--color-text);
text-decoration: none;
overflow-wrap: anywhere;
}
#issue-list .branches {
display: inline-flex;
}
@@ -92,3 +84,47 @@
margin-right: 8px;
text-align: left;
}
/* for "title (#123)":
<span "title-full-link-hover">
<a "title-full-link muted">title (</a>
<a "muted">#123</a>
<a "title-full-link muted">)</a>
</span>
* hover on "title": also highlight the right parentheses
* hover on "#123": don't highlight other parts
*/
.title-full-link-hover:not(:has(:not(.title-full-link):hover)):hover > a.title-full-link {
color: var(--color-primary);
text-decoration: underline;
}
.list-item-large-title {
font-size: 16px;
font-weight: var(--font-weight-semibold);
color: var(--color-text);
text-decoration: none;
overflow-wrap: anywhere;
}
.list-item-title-progress {
width: 200px;
height: 16px;
}
.list-item-secondary-bar {
display: flex;
flex-wrap: wrap;
gap: var(--gap-block);
justify-content: space-between;
color: var(--color-text-light-2);
}
.list-item-secondary-bar a {
color: var(--color-text-light-2);
text-decoration: none;
}
.list-item-secondary-bar a:hover {
color: var(--color-text);
}
+5 -5
View File
@@ -32,7 +32,7 @@
.items-with-main > .item {
display: flex;
gap: 8px;
gap: var(--gap-block);
align-items: flex-start;
}
@@ -44,7 +44,7 @@
.items-with-main > .item .item-main {
display: flex;
flex-direction: column;
gap: 0.25em;
gap: var(--gap-inline);
flex-grow: 1;
flex-basis: 60%; /* avoid wrapping the "item-trailing" too aggressively */
min-width: 0; /* make the "text truncate" work, otherwise the flex axis is not limited and the text just overflows */
@@ -52,7 +52,7 @@
.items-with-main > .item .item-header {
display: flex;
gap: .25rem;
gap: var(--gap-inline);
justify-content: space-between;
flex-wrap: wrap;
}
@@ -63,7 +63,7 @@
.items-with-main > .item .item-trailing {
display: flex;
gap: 0.5rem;
gap: var(--gap-block);
align-items: center;
flex-grow: 0;
flex-wrap: wrap;
@@ -92,7 +92,7 @@
display: flex;
align-items: center;
flex-wrap: wrap;
gap: .25rem;
gap: var(--gap-inline);
color: var(--color-text-light-2);
overflow-wrap: anywhere;
}
-62
View File
@@ -1,62 +0,0 @@
.milestone-list {
list-style: none;
}
.milestone-card {
width: 100%;
padding-top: 10px;
padding-bottom: 10px;
}
.milestone-card + .milestone-card {
border-top: 1px solid var(--color-secondary);
}
.milestone-card .render-content {
padding-top: 10px;
}
.milestone-header progress {
width: 200px;
height: 16px;
}
.milestone-header {
display: flex;
align-items: center;
margin: 0;
flex-wrap: wrap;
justify-content: space-between;
}
.milestone-toolbar {
padding-top: 5px;
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: space-between;
}
.milestone-toolbar .group {
color: var(--color-text-light-2);
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.milestone-toolbar .group > a {
font-size: 15px;
color: var(--color-text-light-2);
}
.milestone-toolbar .group > a:hover {
color: var(--color-text);
}
@media (max-width: 767.98px) {
.milestone-card {
display: flex;
flex-direction: column;
gap: 8px;
}
}
+11 -9
View File
@@ -308,12 +308,12 @@ $.fn.dropdown = function(parameters) {
firstUnfiltered: function() {
module.verbose('Selecting first non-filtered element');
module.remove.selectedItem();
$item
const $selectable = $item
.not(selector.unselectable)
.not(selector.addition + selector.hidden)
.eq(0)
.addClass(className.selected)
;
.not(selector.addition + selector.hidden);
let $selectedItem = $selectable.filter(`[data-value="${CSS.escape($input.val())}"]`); // GITEA-PATCH: try to re-select the last selected item for single selection
if (!$selectedItem.length) $selectedItem = $item.eq(0);
$selectedItem.addClass(className.selected);
},
nextAvailable: function($selected) {
$selected = $selected.eq(0);
@@ -772,11 +772,13 @@ $.fn.dropdown = function(parameters) {
if(!Array.isArray(preSelected)) {
preSelected = preSelected && preSelected!=="" ? preSelected.split(settings.delimiter) : [];
}
$.each(preSelected,function(index,value){
$item.filter('[data-value="'+CSS.escape(value)+'"]') // GITEA-PATCH: use "CSS.escape" for query selector
if (module.is.multiple()) { // GITEA-PATCH: only hide selected items when the dropdown is "multiple selection"
$.each(preSelected, function (index, value) {
$item.filter('[data-value="' + CSS.escape(value) + '"]') // GITEA-PATCH: use "CSS.escape" for query selector
.addClass(className.filtered)
;
});
;
});
}
afterFiltered();
});
}
-1
View File
@@ -47,7 +47,6 @@ function initRepoNewTemplateSearch(form: HTMLFormElement) {
value: String(tmplRepo.repository.id),
});
}
$repoTemplateDropdown.fomanticExt.onResponseKeepSelectedItem($repoTemplateDropdown, inputRepoTemplate.value);
return {results};
},
cache: false,
+8 -8
View File
@@ -13,13 +13,13 @@ test('hideScopedEmptyDividers-simple', () => {
</div>`);
hideScopedEmptyDividers(container);
expect(container.innerHTML).toEqual(`
<div class="divider hidden transition"></div>
<div class="divider hidden"></div>
<div class="item">a</div>
<div class="divider hidden transition"></div>
<div class="divider hidden transition"></div>
<div class="divider hidden"></div>
<div class="divider hidden"></div>
<div class="divider"></div>
<div class="item">b</div>
<div class="divider hidden transition"></div>
<div class="divider hidden"></div>
`);
});
@@ -35,7 +35,7 @@ test('hideScopedEmptyDividers-items-all-filtered', () => {
hideScopedEmptyDividers(container);
expect(container.innerHTML).toEqual(`
<div class="any"></div>
<div class="divider hidden transition"></div>
<div class="divider hidden"></div>
<div class="item filtered">a</div>
<div class="item filtered">b</div>
<div class="divider"></div>
@@ -52,7 +52,7 @@ test('hideScopedEmptyDividers-hide-last', () => {
hideScopedEmptyDividers(container);
expect(container.innerHTML).toEqual(`
<div class="item">a</div>
<div class="divider hidden transition" data-scope="b"></div>
<div class="divider hidden" data-scope="b"></div>
<div class="item tw-hidden" data-scope="b">b</div>
`);
});
@@ -68,9 +68,9 @@ test('hideScopedEmptyDividers-scoped-items', () => {
hideScopedEmptyDividers(container);
expect(container.innerHTML).toEqual(`
<div class="item" data-scope="">a</div>
<div class="divider hidden transition" data-scope="b"></div>
<div class="divider hidden" data-scope="b"></div>
<div class="item tw-hidden" data-scope="b">b</div>
<div class="divider hidden transition" data-scope=""></div>
<div class="divider hidden" data-scope=""></div>
<div class="item" data-scope="">c</div>
`);
});
+2 -19
View File
@@ -8,7 +8,6 @@ const fomanticDropdownFn = $.fn.dropdown;
export function initAriaDropdownPatch() {
if ($.fn.dropdown === ariaDropdownFn) throw new Error('initAriaDropdownPatch could only be called once');
$.fn.dropdown = ariaDropdownFn;
$.fn.fomanticExt.onResponseKeepSelectedItem = onResponseKeepSelectedItem;
$.fn.fomanticExt.onDropdownAfterFiltered = onDropdownAfterFiltered;
(ariaDropdownFn as FomanticInitFunction).settings = fomanticDropdownFn.settings;
}
@@ -296,8 +295,8 @@ export function hideScopedEmptyDividers(container: Element) {
let curScope: string = '', lastVisibleScope: string = '';
const isDivider = (item: Element) => item.classList.contains('divider');
const isScopedDivider = (item: Element) => isDivider(item) && item.hasAttribute('data-scope');
const hideDivider = (item: Element) => item.classList.add('hidden', 'transition'); // dropdown has its own classes to hide items
const showDivider = (item: Element) => item.classList.remove('hidden', 'transition');
const hideDivider = (item: Element) => item.classList.add('hidden'); // dropdown has its own classes to hide items
const showDivider = (item: Element) => item.classList.remove('hidden');
const isHidden = (item: Element) => item.classList.contains('hidden') || item.classList.contains('filtered') || item.classList.contains('tw-hidden');
const handleScopeSwitch = (itemScope: string) => {
if (curScopeVisibleItems.length === 1 && isScopedDivider(curScopeVisibleItems[0])) {
@@ -347,19 +346,3 @@ export function hideScopedEmptyDividers(container: Element) {
if (visibleItems[i + 1].matches('.divider')) hideDivider(visibleItems[i]);
}
}
function onResponseKeepSelectedItem(dropdown: typeof $ | HTMLElement, selectedValue: string) {
// There is a bug in fomantic dropdown when using "apiSettings" to fetch data
// * when there is a selected item, the dropdown insists on hiding the selected one from the list:
// * in the "filter" function: ('[data-value="'+value+'"]').addClass(className.filtered)
//
// When user selects one item, and click the dropdown again,
// then the dropdown only shows other items and will select another (wrong) one.
// It can't be easily fix by using setTimeout(patch, 0) in `onResponse` because the `onResponse` is called before another `setTimeout(..., timeLeft)`
// Fortunately, the "timeLeft" is controlled by "loadingDuration" which is always zero at the moment, so we can use `setTimeout(..., 10)`
const elDropdown = (dropdown instanceof HTMLElement) ? dropdown : (dropdown as any)[0];
setTimeout(() => {
queryElems(elDropdown, `.menu .item[data-value="${CSS.escape(selectedValue)}"].filtered`, (el) => el.classList.remove('filtered'));
$(elDropdown).dropdown('set selected', selectedValue ?? '');
}, 10);
}