Merge pull request #13807 from nocodb/nc-feat/timeline-v2

Nc feat/timeline v2
This commit is contained in:
Raju Udava
2026-05-12 18:58:35 +05:30
committed by GitHub
6 changed files with 50 additions and 31 deletions

View File

@@ -22,7 +22,6 @@ const [useProvideTimelineViewStore, useTimelineViewStore] = useInjectionState(
dateRangeLabel: computed(() => ''),
isPublic: ref(false),
totalRecordCount: ref(0),
recordsWithoutDates: ref(0),
updateFormat: computed(() => ''),
updateRowProperty: async (..._args: any[]) => {},
loadTimelineData: async () => {},

View File

@@ -679,8 +679,13 @@
"day": "Day",
"days": "Days",
"week": "Week",
"twoWeek": "2 Weeks",
"month": "Month",
"quarter": "Quarter",
"sixMonth": "6 Months",
"year": "Year",
"twoYear": "2 Years",
"fiveYear": "5 Years",
"workspace": "Workspace",
"workspaces": "Workspaces",
"document": "Document",

View File

@@ -1,24 +1,2 @@
import type dayjs from 'dayjs'
export function getVisibleDates(..._args: any[]): dayjs.Dayjs[] {
return []
}
export function getBarPosition(..._args: any[]): number {
return 0
}
export function getBarWidth(..._args: any[]): number {
return 0
}
export function isToday(..._args: any[]): boolean {
return false
}
export function isWeekend(..._args: any[]): boolean {
return false
}
export const TIMELINE_GROUP_SIDEBAR_WIDTH = 200
export const TIMELINE_GROUP_HEADER_HEIGHT = 32

View File

@@ -30,6 +30,7 @@ import {
GridViewColumn,
KanbanView,
KanbanViewColumn,
TimelineRange,
View,
} from '~/models';
import { MetaTable } from '~/cli';
@@ -99,7 +100,7 @@ const getAst = async (
};
let coverImageId;
let dependencyFieldsForCalenderView;
let dependencyFieldsForRangeView;
let kanbanGroupColumnId;
let sortColumnIds: string[] = [];
let filterColumnIds: string[] = [];
@@ -115,7 +116,20 @@ const getAst = async (
// coverImageId = calendar.fk_cover_image_col_id;
const calenderRanges = await CalendarRange.read(context, view.id);
if (calenderRanges) {
dependencyFieldsForCalenderView = calenderRanges.ranges
dependencyFieldsForRangeView = calenderRanges.ranges
.flatMap((obj) =>
[obj.fk_from_column_id, (obj as any).fk_to_column_id].filter(Boolean),
)
.map(String);
}
} else if (view && view.type === ViewTypes.TIMELINE) {
// Timeline date columns (start/end) drive the bar position. They are
// typically hidden in the Fields menu, so without explicitly forcing
// them through `allowedCols`, the data response would strip the values
// and the frontend would treat every record as "without dates".
const timelineRanges = await TimelineRange.read(context, view.id);
if (timelineRanges) {
dependencyFieldsForRangeView = timelineRanges.ranges
.flatMap((obj) =>
[obj.fk_from_column_id, (obj as any).fk_to_column_id].filter(Boolean),
)
@@ -192,14 +206,14 @@ const getAst = async (
if (extractOnlyRangeFields) {
const ast: Ast = {
...(dependencyFieldsForCalenderView || []).reduce((o, f) => {
...(dependencyFieldsForRangeView || []).reduce((o, f) => {
const col = model.columns.find((c) => c.id === f);
return { ...o, [getFieldKey(col)]: 1 };
}, {}),
};
await Promise.all(
(dependencyFieldsForCalenderView || []).map((f) =>
(dependencyFieldsForRangeView || []).map((f) =>
extractDependencies(
context,
model.columns.find((c) => c.id === f),
@@ -244,8 +258,8 @@ const getAst = async (
if (coverImageId) {
allowedCols[coverImageId] = 1;
}
if (dependencyFieldsForCalenderView) {
dependencyFieldsForCalenderView.forEach((id) => {
if (dependencyFieldsForRangeView) {
dependencyFieldsForRangeView.forEach((id) => {
allowedCols[id] = 1;
});
}
@@ -418,7 +432,7 @@ const getAst = async (
(!isSystemColumn(col) ||
(!view && isCreatedOrLastModifiedTimeCol(col)) ||
view.show_system_fields ||
(dependencyFieldsForCalenderView ?? []).includes(col.id) ||
(dependencyFieldsForRangeView ?? []).includes(col.id) ||
col.pv) &&
(!fields?.length || isInFields) &&
value;

View File

@@ -2640,6 +2640,16 @@ export default class View implements ViewType {
if (!calendarRangeColumns) break;
if (calendarRangeColumns.includes(column.id)) {
show = true;
} else if (!copyFromView && !column.pv) {
// Fresh timeline views default to a minimal visible set:
// display value (pv) + the configured range columns. Other
// fields stay hidden so the windowed-fetch payload is a few
// fields × N records, not the entire row. Users can opt
// additional fields into the bar via the Fields menu —
// visibility is per-view-column, fully reversible. Skipped
// when duplicating a view so the source's column choices
// carry over.
show = false;
}
}

View File

@@ -13,6 +13,8 @@ import type {
LinkToAnotherRecordColumn,
LookupColumn,
RollupColumn,
TimelineRange,
TimelineView,
} from '~/models';
import type { NcContext } from '~/interface/config';
import {
@@ -74,7 +76,10 @@ export class PublicMetasService {
// todo: return only required props
view.password = undefined;
// Required for Calendar Views
// Required for Calendar / Timeline views — the date columns that drive
// the bar / event positions are usually hidden in the field menu, so the
// visibility filter below would drop them from view.model.columns and
// leave the shared frontend with no range columns to render.
const rangeColumns = [];
if (view.type === ViewTypes.CALENDAR) {
@@ -85,6 +90,14 @@ export class PublicMetasService {
rangeColumns.push((c as any).fk_to_column_id);
}
}
} else if (view.type === ViewTypes.TIMELINE) {
// Timeline ranges can have both from and to date columns.
const timelineRange = ((view.view as TimelineView)?.timeline_range ??
[]) as TimelineRange[];
for (const c of timelineRange) {
if (c.fk_from_column_id) rangeColumns.push(c.fk_from_column_id);
if (c.fk_to_column_id) rangeColumns.push(c.fk_to_column_id);
}
}
view.model.columns = view.columns