+
{{range .Columns}}
diff --git a/templates/repo/projects/view.tmpl b/templates/repo/projects/view.tmpl
index 7267a99b1d..1acaaf8b4b 100644
--- a/templates/repo/projects/view.tmpl
+++ b/templates/repo/projects/view.tmpl
@@ -8,9 +8,7 @@
{{ctx.Locale.Tr "repo.issues.new"}}
-
- {{template "projects/view" .}}
-
+ {{template "projects/view" .}}
{{template "base/footer" .}}
diff --git a/web_src/css/features/projects.css b/web_src/css/features/projects.css
index 72ef523913..80c9d89638 100644
--- a/web_src/css/features/projects.css
+++ b/web_src/css/features/projects.css
@@ -1,31 +1,34 @@
-.board {
+#project-board {
display: flex;
+ align-items: stretch;
flex-direction: row;
flex-wrap: nowrap;
- overflow-x: auto;
- overflow-y: clip;
- align-items: stretch;
+ overflow: auto;
margin: 0 0.5em;
+ max-height: calc(100vh - 120px);
+}
+
+.project-header {
+ padding: 0.5em 0;
+ overflow-x: auto; /* in fullscreen mode, the position is fixed, so we can't use "flex wrap" which would change the height */
+}
+
+.project-header h2 {
+ white-space: nowrap;
+ margin: 0;
}
.project-column {
- background-color: var(--color-project-column-bg) !important;
- border: 1px solid var(--color-secondary) !important;
- border-radius: var(--border-radius);
- margin: 0 0.5rem !important;
- padding: 0.5rem !important;
- width: 320px;
- height: initial;
- min-height: max(calc(100vh - 400px), 300px);
flex: 0 0 auto;
- overflow: visible;
display: flex;
flex-direction: column;
- cursor: default;
-}
-
-.project-column .issue-card {
- color: var(--color-text);
+ background-color: var(--color-project-column-bg);
+ border: 1px solid var(--color-secondary);
+ border-radius: var(--border-radius);
+ margin: 0 0.5rem;
+ padding: 0.5rem;
+ width: 320px;
+ overflow: visible;
}
.project-column-header {
@@ -39,16 +42,15 @@
color: inherit;
}
-.project-column > .cards {
+.project-column > .ui.cards {
flex: 1;
display: flex;
- align-content: baseline;
- margin: 0 !important;
- padding: 0 !important;
- flex-wrap: nowrap !important;
+ flex-wrap: nowrap;
flex-direction: column;
- overflow-x: clip;
+ overflow: clip auto;
gap: .25rem;
+ margin: 0;
+ padding: 0;
}
.project-column > .divider {
@@ -98,3 +100,29 @@
.card-ghost * {
opacity: 0;
}
+
+.fullscreen.projects-view .project-header {
+ position: fixed;
+ z-index: 1000;
+ top: 0;
+ left: 0;
+ right: 0;
+ padding: 0.5em;
+ width: 100%;
+ max-width: 100%;
+ background-color: var(--color-body);
+ border-bottom: 1px solid var(--color-secondary);
+}
+
+/* Hide project-description in full-screen due to its variable height. No need to show it for better space use. */
+.fullscreen.projects-view .project-description {
+ display: none;
+}
+
+.fullscreen.projects-view #project-board {
+ position: absolute;
+ top: 60px;
+ left: 0;
+ right: 0;
+ max-height: calc(100vh - 70px);
+}
diff --git a/web_src/css/modules/menu.css b/web_src/css/modules/menu.css
index a5efd23053..5072dcbd0e 100644
--- a/web_src/css/modules/menu.css
+++ b/web_src/css/modules/menu.css
@@ -1,5 +1,6 @@
.ui.menu {
display: flex;
+ flex-shrink: 0;
margin: 1rem 0;
font-family: var(--fonts-regular);
font-weight: var(--font-weight-normal);
@@ -643,6 +644,7 @@
display: inline-flex;
margin: 0;
vertical-align: middle;
+ flex-shrink: 0;
}
.ui.compact.vertical.menu {
display: inline-block;
diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue
index 5a4c22bc90..447347890b 100644
--- a/web_src/js/components/RepoActionView.vue
+++ b/web_src/js/components/RepoActionView.vue
@@ -7,6 +7,7 @@ import {formatDatetime} from '../utils/time.ts';
import {renderAnsi} from '../render/ansi.ts';
import {POST, DELETE} from '../modules/fetch.ts';
import type {IntervalId} from '../types.ts';
+import {toggleFullScreen} from '../utils.ts';
// see "models/actions/status.go", if it needs to be used somewhere else, move it to a shared file like "types/actions.ts"
type RunStatus = 'unknown' | 'waiting' | 'running' | 'success' | 'failure' | 'cancelled' | 'skipped' | 'blocked';
@@ -416,21 +417,7 @@ export default defineComponent({
toggleFullScreen() {
this.isFullScreen = !this.isFullScreen;
- const fullScreenEl = document.querySelector('.action-view-right');
- const outerEl = document.querySelector('.full.height');
- const actionBodyEl = document.querySelector('.action-view-body');
- const headerEl = document.querySelector('#navbar');
- const contentEl = document.querySelector('.page-content');
- const footerEl = document.querySelector('.page-footer');
- toggleElem(headerEl, !this.isFullScreen);
- toggleElem(contentEl, !this.isFullScreen);
- toggleElem(footerEl, !this.isFullScreen);
- // move .action-view-right to new parent
- if (this.isFullScreen) {
- outerEl.append(fullScreenEl);
- } else {
- actionBodyEl.append(fullScreenEl);
- }
+ toggleFullScreen('.action-view-right', this.isFullScreen, '.action-view-body');
},
async hashChangeListener() {
const selectedLogStep = window.location.hash;
diff --git a/web_src/js/features/repo-projects.ts b/web_src/js/features/repo-projects.ts
index 11f5c19c8d..dc4aa8274b 100644
--- a/web_src/js/features/repo-projects.ts
+++ b/web_src/js/features/repo-projects.ts
@@ -2,8 +2,9 @@ import {contrastColor} from '../utils/color.ts';
import {createSortable} from '../modules/sortable.ts';
import {POST, request} from '../modules/fetch.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
-import {queryElemChildren, queryElems} from '../utils/dom.ts';
+import {queryElemChildren, queryElems, toggleElem} from '../utils/dom.ts';
import type {SortableEvent} from 'sortablejs';
+import {toggleFullScreen} from '../utils.ts';
function updateIssueCount(card: HTMLElement): void {
const parent = card.parentElement;
@@ -34,8 +35,8 @@ async function moveIssue({item, from, to, oldIndex}: SortableEvent): Promise
{
- // the HTML layout is: #project-board > .board > .project-column .cards > .issue-card
- const mainBoard = document.querySelector('#project-board > .board.sortable');
+ // the HTML layout is: #project-board.board > .project-column .cards > .issue-card
+ const mainBoard = document.querySelector('#project-board');
let boardColumns = mainBoard.querySelectorAll('.project-column');
createSortable(mainBoard, {
group: 'project-column',
@@ -139,7 +140,24 @@ function initRepoProjectColumnEdit(writableProjectBoard: Element): void {
});
}
+function initRepoProjectToggleFullScreen(): void {
+ const enterFullscreenBtn = document.querySelector('.screen-full');
+ const exitFullscreenBtn = document.querySelector('.screen-normal');
+ if (!enterFullscreenBtn || !exitFullscreenBtn) return;
+
+ const toggleFullscreenState = (isFullScreen: boolean) => {
+ toggleFullScreen('.projects-view', isFullScreen);
+ toggleElem(enterFullscreenBtn, !isFullScreen);
+ toggleElem(exitFullscreenBtn, isFullScreen);
+ };
+
+ enterFullscreenBtn.addEventListener('click', () => toggleFullscreenState(true));
+ exitFullscreenBtn.addEventListener('click', () => toggleFullscreenState(false));
+}
+
export function initRepoProject(): void {
+ initRepoProjectToggleFullScreen();
+
const writableProjectBoard = document.querySelector('#project-board[data-project-borad-writable="true"]');
if (!writableProjectBoard) return;
diff --git a/web_src/js/utils.ts b/web_src/js/utils.ts
index b825a9339d..e33b1413e8 100644
--- a/web_src/js/utils.ts
+++ b/web_src/js/utils.ts
@@ -1,5 +1,6 @@
import {decode, encode} from 'uint8-to-base64';
import type {IssuePageInfo, IssuePathInfo, RepoOwnerPathInfo} from './types.ts';
+import {toggleClass, toggleElem} from './utils/dom.ts';
// transform /path/to/file.ext to /path/to
export function dirname(path: string): string {
@@ -179,3 +180,24 @@ export function isImageFile({name, type}: {name?: string, type?: string}): boole
export function isVideoFile({name, type}: {name?: string, type?: string}): boolean {
return /\.(mpe?g|mp4|mkv|webm)$/i.test(name || '') || type?.startsWith('video/');
}
+
+export function toggleFullScreen(fullscreenElementsSelector: string, isFullScreen: boolean, sourceParentSelector?: string): void {
+ // hide other elements
+ const headerEl = document.querySelector('#navbar');
+ const contentEl = document.querySelector('.page-content');
+ const footerEl = document.querySelector('.page-footer');
+ toggleElem(headerEl, !isFullScreen);
+ toggleElem(contentEl, !isFullScreen);
+ toggleElem(footerEl, !isFullScreen);
+
+ const sourceParentEl = sourceParentSelector ? document.querySelector(sourceParentSelector) : contentEl;
+
+ const fullScreenEl = document.querySelector(fullscreenElementsSelector);
+ const outerEl = document.querySelector('.full.height');
+ toggleClass(fullscreenElementsSelector, 'fullscreen', isFullScreen);
+ if (isFullScreen) {
+ outerEl.append(fullScreenEl);
+ } else {
+ sourceParentEl.append(fullScreenEl);
+ }
+}