mirror of
https://github.com/go-gitea/gitea.git
synced 2026-04-29 14:31:09 +02:00
add drag-and-drop functionality to org group sidebar
This commit is contained in:
parent
8ea3118e05
commit
5d84f5fbfe
96
web_src/js/features/group.ts
Normal file
96
web_src/js/features/group.ts
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import {createSortable} from '../modules/sortable.ts';
|
||||||
|
import Sortable, {type SortableEvent, type SortableOptions} from 'sortablejs';
|
||||||
|
import {POST} from '../modules/fetch.ts';
|
||||||
|
import {toggleElem} from '../utils/dom.ts';
|
||||||
|
export function initCommonGroup() {
|
||||||
|
if (!document.querySelectorAll('.group').length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelector('.group.settings.options #group_name')?.addEventListener('input', function () {
|
||||||
|
const nameChanged = this.value.toLowerCase() !== this.getAttribute('data-group-name').toLowerCase();
|
||||||
|
toggleElem('#group-name-change-prompt', nameChanged);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function moveItem({item, from, to, oldIndex}: SortableEvent): Promise<void> {
|
||||||
|
const closestUl = to.nodeName.toLowerCase() === 'ul' ? to : to.closest('ul');
|
||||||
|
const sortable = Sortable.get(closestUl);
|
||||||
|
const isGroup = Boolean(item.getAttribute('data-is-group'));
|
||||||
|
const strs = sortable.toArray();
|
||||||
|
const newIndex = Math.max(1, strs.filter((a) => isGroup ?
|
||||||
|
a.toLocaleLowerCase().startsWith('group') :
|
||||||
|
a.toLocaleLowerCase().startsWith('repo')).indexOf(item.getAttribute('data-sort-id')) + 1);
|
||||||
|
const data = {
|
||||||
|
newParent: parseInt(to.closest('ul').closest('li')?.getAttribute('data-id') || '0'),
|
||||||
|
id: parseInt(item.getAttribute('data-id')),
|
||||||
|
newPos: newIndex,
|
||||||
|
isGroup,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
await POST(`${to.getAttribute('data-url')}/items/move`, {
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
from.insertBefore(item, from.children[oldIndex]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function idSortFn(a: string, b: string): number {
|
||||||
|
return parseInt(a.split('-')[2]) - parseInt(b.split('-')[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onEnd(ev: SortableEvent) {
|
||||||
|
const {to} = ev;
|
||||||
|
const closestUl = to.nodeName.toLowerCase() === 'ul' ? to : (to.closest('li')?.closest('ul') ?? to.closest('ul'));
|
||||||
|
const sortable = Sortable.get(closestUl);
|
||||||
|
const strs = sortable.toArray();
|
||||||
|
const groups = strs.filter((a) => a.toLocaleLowerCase().startsWith('group'));
|
||||||
|
const repos = strs.filter((a) => a.toLocaleLowerCase().startsWith('repo'));
|
||||||
|
const newArr = [...groups.toSorted(idSortFn), ...repos.toSorted(idSortFn)];
|
||||||
|
sortable.sort(newArr, true);
|
||||||
|
moveItem(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseSortableOptions: SortableOptions = {
|
||||||
|
group: {
|
||||||
|
name: 'group',
|
||||||
|
put(to, from, drag, ev) {
|
||||||
|
console.debug('to', to);
|
||||||
|
console.debug('from', from);
|
||||||
|
console.debug('drag', drag);
|
||||||
|
console.debug('ev', ev);
|
||||||
|
const closestUl = to.el.nodeName.toLowerCase() === 'ul' ? to.el : to.el.closest('ul');
|
||||||
|
console.debug('put this');
|
||||||
|
return Boolean(closestUl?.getAttribute('data-is-group') ?? true);
|
||||||
|
},
|
||||||
|
pull: true,
|
||||||
|
},
|
||||||
|
dataIdAttr: 'data-sort-id',
|
||||||
|
draggable: '.expandable-menu-item',
|
||||||
|
delayOnTouchOnly: true,
|
||||||
|
delay: 500,
|
||||||
|
// onMove: beforeMove,
|
||||||
|
onEnd,
|
||||||
|
emptyInsertThreshold: 25,
|
||||||
|
};
|
||||||
|
|
||||||
|
function initSubgroupSortable(list: Element) {
|
||||||
|
createSortable(list, {...baseSortableOptions});
|
||||||
|
for (const el of list.querySelectorAll('.expandable-menu-item')) {
|
||||||
|
if (!el.getAttribute('data-is-group')) continue;
|
||||||
|
const sublist = el.querySelector('ul');
|
||||||
|
initSubgroupSortable(sublist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initGroupSortable(parentEl: Element): Promise<void> {
|
||||||
|
initSubgroupSortable(parentEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initGroup(): void {
|
||||||
|
const mainContainer = document.querySelector('#group-navigation-menu > .sortable');
|
||||||
|
if (!mainContainer) return;
|
||||||
|
initGroupSortable(mainContainer);
|
||||||
|
}
|
||||||
@ -67,6 +67,7 @@ import {callInitFunctions} from './modules/init.ts';
|
|||||||
import {initRepoViewFileTree} from './features/repo-view-file-tree.ts';
|
import {initRepoViewFileTree} from './features/repo-view-file-tree.ts';
|
||||||
import {initActionsPermissionsForm} from './features/common-actions-permissions.ts';
|
import {initActionsPermissionsForm} from './features/common-actions-permissions.ts';
|
||||||
import {initGlobalShortcut} from './modules/shortcut.ts';
|
import {initGlobalShortcut} from './modules/shortcut.ts';
|
||||||
|
import {initCommonGroup, initGroup} from "./features/group.ts";
|
||||||
|
|
||||||
const initStartTime = performance.now();
|
const initStartTime = performance.now();
|
||||||
const initPerformanceTracer = callInitFunctions([
|
const initPerformanceTracer = callInitFunctions([
|
||||||
@ -159,6 +160,9 @@ const initPerformanceTracer = callInitFunctions([
|
|||||||
initOAuth2SettingsDisableCheckbox,
|
initOAuth2SettingsDisableCheckbox,
|
||||||
|
|
||||||
initRepoFileView,
|
initRepoFileView,
|
||||||
|
|
||||||
|
initCommonGroup,
|
||||||
|
initGroup,
|
||||||
initActionsPermissionsForm,
|
initActionsPermissionsForm,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -171,16 +175,4 @@ if (initDur > 500) {
|
|||||||
console.error(`slow init functions took ${initDur.toFixed(3)}ms`);
|
console.error(`slow init functions took ${initDur.toFixed(3)}ms`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://htmx.org/events/#htmx:sendError
|
|
||||||
type HtmxEvent = Event & {detail: HtmxResponseInfo};
|
|
||||||
document.body.addEventListener('htmx:sendError', (event) => {
|
|
||||||
// TODO: add translations
|
|
||||||
showErrorToast(`Network error when calling ${(event as HtmxEvent).detail.requestConfig.path}`);
|
|
||||||
});
|
|
||||||
// https://htmx.org/events/#htmx:responseError
|
|
||||||
document.body.addEventListener('htmx:responseError', (event) => {
|
|
||||||
// TODO: add translations
|
|
||||||
showErrorToast(`Error ${(event as HtmxEvent).detail.xhr.status} when calling ${(event as HtmxEvent).detail.requestConfig.path}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.dispatchEvent(new CustomEvent('gitea:index-ready'));
|
document.dispatchEvent(new CustomEvent('gitea:index-ready'));
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user