* the project board was broken, this PR fixes it, and refactor the code, and we prevent the uncategorized column from being dragged. * improve the frontend guideline (as discussed in https://github.com/go-gitea/gitea/pull/17699)tags/v1.16.0-rc1
@@ -40,6 +40,19 @@ We recommend [Google HTML/CSS Style Guide](https://google.github.io/styleguide/h | |||
7. Simple pages and SEO-related pages use Go HTML Template render to generate static Fomantic-UI HTML output. Complex pages can use Vue2 (or Vue3 in future). | |||
### Framework Usage | |||
Mixing different frameworks together is highly discouraged. A JavaScript module should follow one major framework and follow the framework's best practice. | |||
Recommended implementations: | |||
* Vue + Native | |||
* Fomantic-UI (jQuery) | |||
* Native only | |||
Discouraged implementations: | |||
* Vue + jQuery | |||
* jQuery + Native | |||
### `async` Functions | |||
Only mark a function as `async` if and only if there are `await` calls | |||
@@ -98,6 +111,19 @@ $('#el').on('click', async (e) => { // not recommended but acceptable | |||
}); | |||
``` | |||
### HTML Attributes and `dataset` | |||
We forbid `dataset` usage, its camel-casing behaviour makes it hard to grep for attributes. However there are still some special cases, so the current guideline is: | |||
* For legacy code: | |||
* `$.data()` should be refactored to `$.attr()`. | |||
* `$.data()` can be used to bind some non-string data to elements in rare cases, but it is highly discouraged. | |||
* For new code: | |||
* `node.dataset` should not be used, use `node.getAttribute` instead. | |||
* never bind any user data to a DOM node, use a suitable design pattern to describe the relation between node and data. | |||
### Vue2/Vue3 and JSX | |||
Gitea is using Vue2 now, we plan to upgrade to Vue3. We decided not to introduce JSX to keep the HTML and the JavaScript code separated. |
@@ -1,22 +1,24 @@ | |||
const {csrfToken} = window.config; | |||
async function initRepoProjectSortable() { | |||
const els = document.getElementsByClassName('board'); | |||
const els = document.querySelectorAll('#project-board > .board'); | |||
if (!els.length) return; | |||
const {Sortable} = await import(/* webpackChunkName: "sortable" */'sortablejs'); | |||
const boardColumns = document.getElementsByClassName('board-column'); | |||
new Sortable(els[0], { | |||
// the HTML layout is: #project-board > .board > .board-column .board.cards > .board-card.card .content | |||
const mainBoard = els[0]; | |||
let boardColumns = mainBoard.getElementsByClassName('board-column'); | |||
new Sortable(mainBoard, { | |||
group: 'board-column', | |||
draggable: '.board-column', | |||
filter: '[data-id="0"]', | |||
animation: 150, | |||
ghostClass: 'card-ghost', | |||
onSort: () => { | |||
const board = document.getElementsByClassName('board')[0]; | |||
const boardColumns = board.getElementsByClassName('board-column'); | |||
for (const [i, column] of boardColumns.entries()) { | |||
boardColumns = mainBoard.getElementsByClassName('board-column'); | |||
for (let i = 0; i < boardColumns.length; i++) { | |||
const column = boardColumns[i]; | |||
if (parseInt($(column).data('sorting')) !== i) { | |||
$.ajax({ | |||
url: $(column).data('url'), | |||
@@ -33,8 +35,9 @@ async function initRepoProjectSortable() { | |||
}, | |||
}); | |||
for (const column of boardColumns) { | |||
new Sortable(column.getElementsByClassName('board')[0], { | |||
for (const boardColumn of boardColumns) { | |||
const boardCardList = boardColumn.getElementsByClassName('board')[0]; | |||
new Sortable(boardCardList, { | |||
group: 'shared', | |||
animation: 150, | |||
ghostClass: 'card-ghost', |