Skip to content

Commit 77c8297

Browse files
committed
Add vertical scroll and position saving
1 parent 24fdd10 commit 77c8297

3 files changed

Lines changed: 92 additions & 32 deletions

File tree

src/kanban-view.ts

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
BACKGROUND_BRIGHTNESS_OPTION_KEY,
1919
BACKGROUND_IMAGE_OPTION_KEY,
2020
BOARD_SCROLL_POSITION_KEY,
21+
BOARD_SCROLL_TOP_POSITION_KEY,
2122
COLUMN_ORDER_OPTION_KEY,
2223
COLUMN_TRANSPARENCY_OPTION_KEY,
2324
LOCAL_CARD_ORDER_OPTION_KEY,
@@ -207,11 +208,11 @@ export class KanbanView extends BasesView {
207208

208209
this.refreshElementIndexes();
209210

210-
const scrollLeftToRestore =
211-
previousBoardScrollLeft > 0
212-
? previousBoardScrollLeft
213-
: this.loadBoardScrollPosition();
214-
this.restoreBoardScrollLeft(scrollLeftToRestore);
211+
const { scrollLeft: scrollLeftToRestore, scrollTop: scrollTopToRestore } =
212+
this.loadBoardScrollPosition();
213+
const finalScrollLeft =
214+
previousBoardScrollLeft > 0 ? previousBoardScrollLeft : scrollLeftToRestore;
215+
this.restoreBoardScrollPosition(finalScrollLeft, scrollTopToRestore);
215216
}
216217

217218
private getBoardScrollLeft(): number {
@@ -225,53 +226,82 @@ export class KanbanView extends BasesView {
225226
return boardEl.scrollLeft;
226227
}
227228

228-
private restoreBoardScrollLeft(scrollLeft: number): void {
229-
if (scrollLeft <= 0) {
230-
return;
231-
}
232-
229+
private restoreBoardScrollPosition(scrollLeft: number, scrollTop: number): void {
233230
const boardEl = this.rootEl.querySelector<HTMLElement>(
234231
".bases-kanban-board",
235232
);
236233
if (boardEl === null) {
237234
return;
238235
}
239236

240-
boardEl.scrollLeft = scrollLeft;
237+
if (scrollLeft > 0) {
238+
boardEl.scrollLeft = scrollLeft;
239+
}
240+
if (scrollTop > 0) {
241+
boardEl.scrollTop = scrollTop;
242+
}
243+
241244
window.requestAnimationFrame(() => {
242245
if (this.rootEl.contains(boardEl)) {
243-
boardEl.scrollLeft = scrollLeft;
246+
if (scrollLeft > 0) {
247+
boardEl.scrollLeft = scrollLeft;
248+
}
249+
if (scrollTop > 0) {
250+
boardEl.scrollTop = scrollTop;
251+
}
244252
}
245253
});
246254
}
247255

248256
private setupBoardScrollListener(boardEl: HTMLElement): void {
249257
boardEl.addEventListener("scroll", () => {
250-
this.debouncedSaveBoardScrollPosition(boardEl.scrollLeft);
258+
this.debouncedSaveBoardScrollPosition(
259+
boardEl.scrollLeft,
260+
boardEl.scrollTop,
261+
);
251262
});
252263
}
253264

254-
private debouncedSaveBoardScrollPosition(scrollLeft: number): void {
265+
private debouncedSaveBoardScrollPosition(
266+
scrollLeft: number,
267+
scrollTop: number,
268+
): void {
255269
if (this.scrollSaveTimeout !== null) {
256270
window.clearTimeout(this.scrollSaveTimeout);
257271
}
258272
this.scrollSaveTimeout = window.setTimeout(() => {
259-
this.saveBoardScrollPosition(scrollLeft);
273+
this.saveBoardScrollPosition(scrollLeft, scrollTop);
260274
this.scrollSaveTimeout = null;
261275
}, this.plugin.settings.scrollDebounceMs);
262276
}
263277

264-
private saveBoardScrollPosition(scrollLeft: number): void {
278+
private saveBoardScrollPosition(scrollLeft: number, scrollTop: number): void {
265279
this.config?.set(BOARD_SCROLL_POSITION_KEY, String(scrollLeft));
280+
this.config?.set(BOARD_SCROLL_TOP_POSITION_KEY, String(scrollTop));
266281
}
267282

268-
private loadBoardScrollPosition(): number {
269-
const configValue = this.config?.get(BOARD_SCROLL_POSITION_KEY);
270-
if (typeof configValue !== "string" || configValue.length === 0) {
271-
return 0;
283+
private loadBoardScrollPosition(): { scrollLeft: number; scrollTop: number } {
284+
const scrollLeftValue = this.config?.get(BOARD_SCROLL_POSITION_KEY);
285+
const scrollTopValue = this.config?.get(BOARD_SCROLL_TOP_POSITION_KEY);
286+
287+
let scrollLeft = 0;
288+
let scrollTop = 0;
289+
290+
if (typeof scrollLeftValue === "string" && scrollLeftValue.length > 0) {
291+
const parsedLeft = Number.parseInt(scrollLeftValue, 10);
292+
if (!Number.isNaN(parsedLeft)) {
293+
scrollLeft = parsedLeft;
294+
}
272295
}
273-
const parsed = Number.parseInt(configValue, 10);
274-
return Number.isNaN(parsed) ? 0 : parsed;
296+
297+
if (typeof scrollTopValue === "string" && scrollTopValue.length > 0) {
298+
const parsedTop = Number.parseInt(scrollTopValue, 10);
299+
if (!Number.isNaN(parsedTop)) {
300+
scrollTop = parsedTop;
301+
}
302+
}
303+
304+
return { scrollLeft, scrollTop };
275305
}
276306

277307
private renderPlaceholder(): void {

src/kanban-view/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export const NO_VALUE_COLUMN_KEY = "__bases_kanban_no_value__";
33
export const COLUMN_ORDER_OPTION_KEY = "columnOrder";
44
export const LOCAL_CARD_ORDER_OPTION_KEY = "localCardOrder";
55
export const BOARD_SCROLL_POSITION_KEY = "boardScrollPosition";
6+
export const BOARD_SCROLL_TOP_POSITION_KEY = "boardScrollTopPosition";
67
export const BACKGROUND_IMAGE_OPTION_KEY = "backgroundImage";
78
export const BACKGROUND_BRIGHTNESS_OPTION_KEY = "backgroundBrightness";
89
export const BACKGROUND_BLUR_OPTION_KEY = "backgroundBlur";

styles.css

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,54 @@
11
.bases-kanban-container {
22
width: 100%;
3-
height: auto;
3+
height: 100%;
4+
min-height: 100%;
5+
display: flex;
6+
flex-direction: column;
47
overflow: hidden;
58
padding: var(--size-4-3);
69
position: relative;
10+
box-sizing: border-box;
711
}
812

913
/* Background layer with image, blur and brightness */
1014
.bases-kanban-background {
11-
position: absolute;
12-
top: 0;
13-
left: 0;
14-
right: 0;
15-
bottom: 0;
15+
position: fixed;
16+
inset: 0;
17+
width: 100vw;
18+
height: 100vh;
1619
background-size: cover;
1720
background-position: center;
1821
background-repeat: no-repeat;
1922
z-index: 0;
2023
pointer-events: none;
2124
}
2225

26+
/* Dark scrim overlay for readability */
27+
.bases-kanban-background::after {
28+
content: "";
29+
position: absolute;
30+
inset: 0;
31+
background: radial-gradient(
32+
ellipse at center,
33+
transparent 0%,
34+
rgba(0, 0, 0, 0.1) 50%,
35+
rgba(0, 0, 0, 0.3) 100%
36+
);
37+
pointer-events: none;
38+
}
39+
2340
/* Column with transparency support */
2441
.bases-kanban-column {
2542
flex: 0 0 var(--bases-kanban-column-width, 280px);
2643
background: color-mix(in srgb, var(--background-secondary), transparent calc(100% - var(--bases-kanban-column-transparency, 1) * 100%));
2744
border-radius: var(--radius-m);
2845
display: flex;
2946
flex-direction: column;
30-
overflow: hidden;
47+
overflow: visible;
3148
position: relative;
49+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.05);
50+
backdrop-filter: blur(8px);
51+
border: 1px solid var(--background-modifier-border);
3252
}
3353

3454
.bases-kanban-placeholder {
@@ -42,12 +62,14 @@
4262
.bases-kanban-board {
4363
display: flex;
4464
gap: var(--size-4-3);
45-
height: 100%;
65+
flex: 1;
66+
min-height: 0;
4667
overflow-x: auto;
47-
overflow-y: hidden;
68+
overflow-y: auto;
4869
outline: none;
4970
position: relative;
5071
z-index: 1;
72+
align-items: flex-start;
5173
}
5274

5375
.bases-kanban-column-header {
@@ -117,18 +139,25 @@
117139
}
118140

119141
.bases-kanban-cards {
120-
overflow-y: auto;
121142
padding: var(--size-4-2);
122143
display: flex;
123144
flex-direction: column;
124145
gap: var(--size-4-2);
146+
flex: 1;
125147
}
126148

127149
.bases-kanban-card {
128150
background: var(--background-primary);
129151
border: 1px solid var(--background-modifier-border);
130152
border-radius: var(--radius-s);
131153
padding: var(--size-4-2) var(--size-4-3);
154+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04);
155+
transition: box-shadow 0.15s ease, transform 0.1s ease;
156+
}
157+
158+
.bases-kanban-card:hover {
159+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06);
160+
transform: translateY(-1px);
132161
}
133162

134163
.bases-kanban-card.bases-kanban-card-selected {

0 commit comments

Comments
 (0)