Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove jQuery class from the repository topic box #30191

Merged
merged 18 commits into from
Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor
  • Loading branch information
wxiaoguang committed Mar 31, 2024
commit bf7319c5bc184e87fedf049c223c45db2677a8ed
23 changes: 11 additions & 12 deletions templates/repo/home.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,21 @@
</div>
</form>
</div>
<div class="tw-flex tw-items-center tw-flex-wrap tw-gap-1" id="repo-topics">
{{range .Topics}}<a class="ui repo-topic large label topic tw-m-0" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
<div class="tw-flex tw-items-center tw-flex-wrap tw-gap-2" id="repo-topics">
{{/* it should match the code in issue-home.js */}}
{{range .Topics}}<a class="repo-topic ui large label" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<button id="manage_topic" class="btn interact-fg tw-text-12">{{ctx.Locale.Tr "repo.topic.manage_topics"}}</button>{{end}}
</div>
{{end}}
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}
<div class="ui form tw-hidden tw-flex tw-flex-col tw-mt-4" id="topic_edit">
<div class="field tw-flex-1 tw-mb-1">
<div class="ui fluid multiple search selection dropdown tw-flex-wrap" data-text-count-prompt="{{ctx.Locale.Tr "repo.topic.count_prompt"}}" data-text-format-prompt="{{ctx.Locale.Tr "repo.topic.format_prompt"}}">
<input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if Eval $i "+" 1 "<" (len $.Topics)}},{{end}}{{end}}">
{{range .Topics}}
{{/* keey the same layout as Fomantic UI generated labels */}}
<a class="ui label transition visible tw-cursor-default tw-inline-block" data-value="{{.Name}}">{{.Name}}{{svg "octicon-x" 16 "delete icon"}}</a>
{{end}}
<div class="text"></div>
</div>
<div class="ui form tw-hidden tw-flex tw-gap-2" id="topic_edit">
<div class="ui fluid multiple search selection dropdown tw-flex-wrap tw-flex-1" data-text-count-prompt="{{ctx.Locale.Tr "repo.topic.count_prompt"}}" data-text-format-prompt="{{ctx.Locale.Tr "repo.topic.format_prompt"}}">
<input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if Eval $i "+" 1 "<" (len $.Topics)}},{{end}}{{end}}">
{{range .Topics}}
{{/* keey the same layout as Fomantic UI generated labels */}}
<a class="ui label transition visible tw-cursor-default tw-inline-block" data-value="{{.Name}}">{{.Name}}{{svg "octicon-x" 16 "delete icon"}}</a>
{{end}}
<div class="text"></div>
</div>
<div>
<button class="ui basic button" id="cancel_topic_edit">{{ctx.Locale.Tr "cancel"}}</button>
Expand Down
1 change: 1 addition & 0 deletions web_src/css/repo.css
Original file line number Diff line number Diff line change
Expand Up @@ -2517,6 +2517,7 @@ tbody.commit-list {
#repo-topics .repo-topic {
font-weight: var(--font-weight-normal);
cursor: pointer;
margin: 0;
}

#new-dependency-drop-list.ui.selection.dropdown {
Expand Down
68 changes: 15 additions & 53 deletions web_src/js/features/repo-home.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,37 @@
import $ from 'jquery';
import {stripTags} from '../utils.js';
import {hideElem, showElem} from '../utils/dom.js';
import {hideElem, queryElemChildren, showElem} from '../utils/dom.js';
import {POST} from '../modules/fetch.js';

const {appSubUrl} = window.config;

export function initRepoTopicBar() {
const mgrBtn = document.getElementById('manage_topic');
if (!mgrBtn) return;

const editDiv = document.getElementById('topic_edit');
const viewDiv = document.getElementById('repo-topics');
const saveBtn = document.getElementById('save_topic');
const topicDropdown = editDiv.querySelector('.dropdown');
const $topicForm = $(editDiv);
/**
* @type {HTMLInputElement}
*/
const topicDropdownSearch = topicDropdown.querySelector('input.search');
const topicDropdown = editDiv.querySelector('.ui.dropdown');
const topicPrompts = {
countPrompt: topicDropdown.getAttribute('data-text-count-prompt') ?? undefined,
formatPrompt: topicDropdown.getAttribute('data-text-format-prompt') ?? undefined,
countPrompt: topicDropdown.getAttribute('data-text-count-prompt'),
formatPrompt: topicDropdown.getAttribute('data-text-format-prompt'),
};

mgrBtn.addEventListener('click', () => {
hideElem(viewDiv);
showElem(editDiv);
topicDropdownSearch.focus();
topicDropdown.querySelector('input.search').focus();
});

$('#cancel_topic_edit').on('click', () => {
document.querySelector('#cancel_topic_edit').addEventListener('click', () => {
hideElem(editDiv);
showElem(viewDiv);
mgrBtn.focus();
});

saveBtn.addEventListener('click', async () => {
const topics = $('input[name=topics]').val();
const topics = editDiv.querySelector('input[name=topics]').value;

const data = new FormData();
data.append('topics', topics);
Expand All @@ -45,13 +41,14 @@ export function initRepoTopicBar() {
if (response.ok) {
const responseData = await response.json();
if (responseData.status === 'ok') {
$(viewDiv).children('.topic').remove();
queryElemChildren(viewDiv, '.repo-topic', (el) => el.remove());
if (topics.length) {
const topicArray = topics.split(',');
topicArray.sort();
for (const topic of topicArray) {
// it should match the code in repo/home.tmpl
const link = document.createElement('a');
link.classList.add('ui', 'repo-topic', 'large', 'label', 'topic', 'tw-m-0');
link.classList.add('repo-topic', 'ui', 'large', 'label');
link.href = `${appSubUrl}/explore/repos?q=${encodeURIComponent(topic)}&topic=1`;
link.textContent = topic;
mgrBtn.parentNode.insertBefore(link, mgrBtn); // insert all new topics before manage button
Expand All @@ -66,7 +63,7 @@ export function initRepoTopicBar() {
topicPrompts.formatPrompt = responseData.message;

const {invalidTopics} = responseData;
const topicLabels = topicDropdown.querySelectorAll('a.ui.label');
const topicLabels = queryElemChildren(topicDropdown,'a.ui.label');
for (const [index, value] of topics.split(',').entries()) {
if (invalidTopics.includes(value)) {
topicLabels[index].classList.remove('green');
Expand All @@ -77,9 +74,6 @@ export function initRepoTopicBar() {
topicPrompts.countPrompt = responseData.message;
}
}

// Always validate the form
$topicForm.form('validate form');
});

$(topicDropdown).dropdown({
Expand All @@ -105,7 +99,7 @@ export function initRepoTopicBar() {
const query = stripTags(this.urlData.query.trim());
let found_query = false;
const current_topics = [];
for (const el of topicDropdown.querySelectorAll('a.label.visible')) {
for (const el of queryElemChildren(topicDropdown, 'a.ui.label.visible')) {
current_topics.push(el.getAttribute('data-value'));
}

Expand Down Expand Up @@ -149,40 +143,8 @@ export function initRepoTopicBar() {
},
onAdd(addedValue, _addedText, $addedChoice) {
addedValue = addedValue.toLowerCase().trim();
$($addedChoice)[0].setAttribute('data-value', addedValue);
$($addedChoice)[0].setAttribute('data-text', addedValue);
},
});

$.fn.form.settings.rules.validateTopic = function (_values, regExp) {
const topics = topicDropdown.querySelectorAll(':scope > a.ui.label');
const lastTopic = topics[topics.length - 1];
const isLastTopicValid = lastTopic?.getAttribute('data-value').match(regExp);
if (lastTopic && !isLastTopicValid) {
lastTopic.classList.remove('green');
lastTopic.classList.add('red');
}
return topicDropdown.querySelectorAll('a.ui.label.red').length === 0;
};

$topicForm.form({
on: 'change',
inline: true,
fields: {
topics: {
identifier: 'topics',
rules: [
{
type: 'validateTopic',
value: /^\s*[a-z0-9][-.a-z0-9]{0,35}\s*$/,
prompt: topicPrompts.formatPrompt,
},
{
type: 'maxCount[25]',
prompt: topicPrompts.countPrompt,
},
],
},
$addedChoice[0].setAttribute('data-value', addedValue);
$addedChoice[0].setAttribute('data-text', addedValue);
},
});
}
18 changes: 16 additions & 2 deletions web_src/js/utils/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,22 @@ export function isElemHidden(el) {
return res[0];
}

export function queryElemSiblings(el, selector = '*') {
return Array.from(el.parentNode.children).filter((child) => child !== el && child.matches(selector));
function applyElemsCallback(elems, fn) {
if (fn) {
for (const el of elems) {
fn.call(el, el); // call the function with the element as "this", and pass the element as the first argument
}
wxiaoguang marked this conversation as resolved.
Show resolved Hide resolved
silverwind marked this conversation as resolved.
Show resolved Hide resolved
}
return elems;
}

export function queryElemSiblings(el, selector = '*', fn = null) {
silverwind marked this conversation as resolved.
Show resolved Hide resolved
return applyElemsCallback(Array.from(el.parentNode.children).filter((child) => child !== el && child.matches(selector)), fn);
}

// it works like jQuery.children: only the direct children are selected
export function queryElemChildren(parent, selector, fn = null) {
yardenshoham marked this conversation as resolved.
Show resolved Hide resolved
return applyElemsCallback(parent.querySelectorAll(`:scope > ${selector}`), fn);
}

export function onDomReady(cb) {
Expand Down
Loading