mirror of
https://github.com/zadam/trilium.git
synced 2025-10-20 23:29:02 +02:00
refactor(views/table): split column & rows into separate file
This commit is contained in:
parent
28a755306a
commit
af296a1e4e
110
apps/client/src/widgets/view_widgets/table_view/columns.ts
Normal file
110
apps/client/src/widgets/view_widgets/table_view/columns.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import { RelationEditor } from "./relation_editor.js";
|
||||
import { NoteFormatter, NoteTitleFormatter } from "./formatters.js";
|
||||
import { applyHeaderMenu } from "./header-menu.js";
|
||||
import type { ColumnDefinition } from "tabulator-tables";
|
||||
import { LabelType } from "../../../services/promoted_attribute_definition_parser.js";
|
||||
|
||||
type ColumnType = LabelType | "relation";
|
||||
|
||||
export interface PromotedAttributeInformation {
|
||||
name: string;
|
||||
title?: string;
|
||||
type?: ColumnType;
|
||||
}
|
||||
|
||||
const labelTypeMappings: Record<ColumnType, Partial<ColumnDefinition>> = {
|
||||
text: {
|
||||
editor: "input"
|
||||
},
|
||||
boolean: {
|
||||
formatter: "tickCross",
|
||||
editor: "tickCross"
|
||||
},
|
||||
date: {
|
||||
editor: "date",
|
||||
},
|
||||
datetime: {
|
||||
editor: "datetime"
|
||||
},
|
||||
number: {
|
||||
editor: "number"
|
||||
},
|
||||
time: {
|
||||
editor: "input"
|
||||
},
|
||||
url: {
|
||||
formatter: "link",
|
||||
editor: "input"
|
||||
},
|
||||
relation: {
|
||||
editor: RelationEditor,
|
||||
formatter: NoteFormatter
|
||||
}
|
||||
};
|
||||
|
||||
export function buildColumnDefinitions(info: PromotedAttributeInformation[], existingColumnData?: ColumnDefinition[]) {
|
||||
const columnDefs: ColumnDefinition[] = [
|
||||
{
|
||||
title: "#",
|
||||
formatter: "rownum",
|
||||
headerSort: false,
|
||||
hozAlign: "center",
|
||||
resizable: false,
|
||||
frozen: true
|
||||
},
|
||||
{
|
||||
field: "noteId",
|
||||
title: "Note ID",
|
||||
visible: false
|
||||
},
|
||||
{
|
||||
field: "title",
|
||||
title: "Title",
|
||||
editor: "input",
|
||||
formatter: NoteTitleFormatter,
|
||||
width: 400
|
||||
}
|
||||
];
|
||||
|
||||
const seenFields = new Set<string>();
|
||||
for (const { name, title, type } of info) {
|
||||
const prefix = (type === "relation" ? "relations" : "labels");
|
||||
const field = `${prefix}.${name}`;
|
||||
|
||||
if (seenFields.has(field)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
columnDefs.push({
|
||||
field,
|
||||
title: title ?? name,
|
||||
editor: "input",
|
||||
...labelTypeMappings[type ?? "text"],
|
||||
});
|
||||
seenFields.add(field);
|
||||
}
|
||||
|
||||
applyHeaderMenu(columnDefs);
|
||||
if (existingColumnData) {
|
||||
restoreExistingData(columnDefs, existingColumnData);
|
||||
}
|
||||
|
||||
return columnDefs;
|
||||
}
|
||||
|
||||
function restoreExistingData(newDefs: ColumnDefinition[], oldDefs: ColumnDefinition[]) {
|
||||
const byField = new Map<string, ColumnDefinition>;
|
||||
for (const def of oldDefs) {
|
||||
byField.set(def.field ?? "", def);
|
||||
}
|
||||
|
||||
for (const newDef of newDefs) {
|
||||
const oldDef = byField.get(newDef.field ?? "");
|
||||
if (!oldDef) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newDef.width = oldDef.width;
|
||||
newDef.visible = oldDef.visible;
|
||||
}
|
||||
}
|
@ -1,194 +0,0 @@
|
||||
import FNote from "../../../entities/fnote.js";
|
||||
import type { LabelType } from "../../../services/promoted_attribute_definition_parser.js";
|
||||
import type { ColumnDefinition } from "tabulator-tables";
|
||||
import { RelationEditor } from "./relation_editor.js";
|
||||
import { NoteFormatter, NoteTitleFormatter } from "./formatters.js";
|
||||
import { applyHeaderMenu } from "./header-menu.js";
|
||||
|
||||
export type TableData = {
|
||||
iconClass: string;
|
||||
noteId: string;
|
||||
title: string;
|
||||
labels: Record<string, boolean | string | null>;
|
||||
relations: Record<string, boolean | string | null>;
|
||||
branchId: string;
|
||||
};
|
||||
|
||||
type ColumnType = LabelType | "relation";
|
||||
|
||||
export interface PromotedAttributeInformation {
|
||||
name: string;
|
||||
title?: string;
|
||||
type?: ColumnType;
|
||||
}
|
||||
|
||||
const labelTypeMappings: Record<ColumnType, Partial<ColumnDefinition>> = {
|
||||
text: {
|
||||
editor: "input"
|
||||
},
|
||||
boolean: {
|
||||
formatter: "tickCross",
|
||||
editor: "tickCross"
|
||||
},
|
||||
date: {
|
||||
editor: "date",
|
||||
},
|
||||
datetime: {
|
||||
editor: "datetime"
|
||||
},
|
||||
number: {
|
||||
editor: "number"
|
||||
},
|
||||
time: {
|
||||
editor: "input"
|
||||
},
|
||||
url: {
|
||||
formatter: "link",
|
||||
editor: "input"
|
||||
},
|
||||
relation: {
|
||||
editor: RelationEditor,
|
||||
formatter: NoteFormatter
|
||||
}
|
||||
};
|
||||
|
||||
type GridLabelType = 'text' | 'number' | 'boolean' | 'date' | 'dateString' | 'object';
|
||||
|
||||
export async function buildData(parentNote: FNote, info: PromotedAttributeInformation[], notes: FNote[]) {
|
||||
const columnDefs = buildColumnDefinitions(info);
|
||||
const rowData = await buildRowDefinitions(parentNote, notes, info);
|
||||
|
||||
return {
|
||||
rowData,
|
||||
columnDefs
|
||||
}
|
||||
}
|
||||
|
||||
export function buildColumnDefinitions(info: PromotedAttributeInformation[], existingColumnData?: ColumnDefinition[]) {
|
||||
const columnDefs: ColumnDefinition[] = [
|
||||
{
|
||||
title: "#",
|
||||
formatter: "rownum",
|
||||
headerSort: false,
|
||||
hozAlign: "center",
|
||||
resizable: false,
|
||||
frozen: true
|
||||
},
|
||||
{
|
||||
field: "noteId",
|
||||
title: "Note ID",
|
||||
visible: false
|
||||
},
|
||||
{
|
||||
field: "title",
|
||||
title: "Title",
|
||||
editor: "input",
|
||||
formatter: NoteTitleFormatter,
|
||||
width: 400
|
||||
}
|
||||
];
|
||||
|
||||
const seenFields = new Set<string>();
|
||||
for (const { name, title, type } of info) {
|
||||
const prefix = (type === "relation" ? "relations" : "labels");
|
||||
const field = `${prefix}.${name}`;
|
||||
|
||||
if (seenFields.has(field)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
columnDefs.push({
|
||||
field,
|
||||
title: title ?? name,
|
||||
editor: "input",
|
||||
...labelTypeMappings[type ?? "text"],
|
||||
});
|
||||
seenFields.add(field);
|
||||
}
|
||||
|
||||
applyHeaderMenu(columnDefs);
|
||||
if (existingColumnData) {
|
||||
restoreExistingData(columnDefs, existingColumnData);
|
||||
}
|
||||
|
||||
return columnDefs;
|
||||
}
|
||||
|
||||
function restoreExistingData(newDefs: ColumnDefinition[], oldDefs: ColumnDefinition[]) {
|
||||
const byField = new Map<string, ColumnDefinition>;
|
||||
for (const def of oldDefs) {
|
||||
byField.set(def.field, def);
|
||||
}
|
||||
|
||||
for (const newDef of newDefs) {
|
||||
const oldDef = byField.get(newDef.field);
|
||||
if (!oldDef) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newDef.width = oldDef.width;
|
||||
newDef.visible = oldDef.visible;
|
||||
}
|
||||
}
|
||||
|
||||
export async function buildRowDefinitions(parentNote: FNote, notes: FNote[], infos: PromotedAttributeInformation[]) {
|
||||
const definitions: TableData[] = [];
|
||||
for (const branch of parentNote.getChildBranches()) {
|
||||
const note = await branch.getNote();
|
||||
if (!note) {
|
||||
continue; // Skip if the note is not found
|
||||
}
|
||||
|
||||
const labels: typeof definitions[0]["labels"] = {};
|
||||
const relations: typeof definitions[0]["relations"] = {};
|
||||
for (const { name, type } of infos) {
|
||||
if (type === "relation") {
|
||||
relations[name] = note.getRelationValue(name);
|
||||
} else if (type === "boolean") {
|
||||
labels[name] = note.hasLabel(name);
|
||||
} else {
|
||||
labels[name] = note.getLabelValue(name);
|
||||
}
|
||||
}
|
||||
definitions.push({
|
||||
iconClass: note.getIcon(),
|
||||
noteId: note.noteId,
|
||||
title: note.title,
|
||||
labels,
|
||||
relations,
|
||||
branchId: branch.branchId
|
||||
});
|
||||
}
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
export default function getPromotedAttributeInformation(parentNote: FNote) {
|
||||
const info: PromotedAttributeInformation[] = [];
|
||||
for (const promotedAttribute of parentNote.getPromotedDefinitionAttributes()) {
|
||||
const def = promotedAttribute.getDefinition();
|
||||
if (def.multiplicity !== "single") {
|
||||
console.warn("Multiple values are not supported for now");
|
||||
continue;
|
||||
}
|
||||
|
||||
const [ labelType, name ] = promotedAttribute.name.split(":", 2);
|
||||
if (promotedAttribute.type !== "label") {
|
||||
console.warn("Relations are not supported for now");
|
||||
continue;
|
||||
}
|
||||
|
||||
let type: LabelType | "relation" = def.labelType || "text";
|
||||
if (labelType === "relation") {
|
||||
type = "relation";
|
||||
}
|
||||
|
||||
info.push({
|
||||
name,
|
||||
title: def.promotedAlias,
|
||||
type
|
||||
});
|
||||
}
|
||||
console.log("Promoted attribute information", info);
|
||||
return info;
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
import froca from "../../../services/froca.js";
|
||||
import ViewMode, { type ViewModeArgs } from "../view_mode.js";
|
||||
import attributes, { setAttribute, setLabel } from "../../../services/attributes.js";
|
||||
import getPromotedAttributeInformation, { buildColumnDefinitions, buildData, buildRowDefinitions, TableData } from "./data.js";
|
||||
import server from "../../../services/server.js";
|
||||
import SpacedUpdate from "../../../services/spaced_update.js";
|
||||
import type { CommandListenerData, EventData } from "../../../components/app_context.js";
|
||||
@ -11,6 +10,8 @@ import {Tabulator, SortModule, FormatModule, InteractionModule, EditModule, Resi
|
||||
import "tabulator-tables/dist/css/tabulator_bootstrap5.min.css";
|
||||
import { canReorderRows, configureReorderingRows } from "./dragging.js";
|
||||
import buildFooter from "./footer.js";
|
||||
import getPromotedAttributeInformation, { buildRowDefinitions } from "./rows.js";
|
||||
import { buildColumnDefinitions } from "./columns.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<div class="table-view">
|
||||
|
74
apps/client/src/widgets/view_widgets/table_view/rows.ts
Normal file
74
apps/client/src/widgets/view_widgets/table_view/rows.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import FNote from "../../../entities/fnote.js";
|
||||
import type { LabelType } from "../../../services/promoted_attribute_definition_parser.js";
|
||||
import type { PromotedAttributeInformation } from "./columns.js";
|
||||
|
||||
export type TableData = {
|
||||
iconClass: string;
|
||||
noteId: string;
|
||||
title: string;
|
||||
labels: Record<string, boolean | string | null>;
|
||||
relations: Record<string, boolean | string | null>;
|
||||
branchId: string;
|
||||
};
|
||||
|
||||
export async function buildRowDefinitions(parentNote: FNote, notes: FNote[], infos: PromotedAttributeInformation[]) {
|
||||
const definitions: TableData[] = [];
|
||||
for (const branch of parentNote.getChildBranches()) {
|
||||
const note = await branch.getNote();
|
||||
if (!note) {
|
||||
continue; // Skip if the note is not found
|
||||
}
|
||||
|
||||
const labels: typeof definitions[0]["labels"] = {};
|
||||
const relations: typeof definitions[0]["relations"] = {};
|
||||
for (const { name, type } of infos) {
|
||||
if (type === "relation") {
|
||||
relations[name] = note.getRelationValue(name);
|
||||
} else if (type === "boolean") {
|
||||
labels[name] = note.hasLabel(name);
|
||||
} else {
|
||||
labels[name] = note.getLabelValue(name);
|
||||
}
|
||||
}
|
||||
definitions.push({
|
||||
iconClass: note.getIcon(),
|
||||
noteId: note.noteId,
|
||||
title: note.title,
|
||||
labels,
|
||||
relations,
|
||||
branchId: branch.branchId
|
||||
});
|
||||
}
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
export default function getPromotedAttributeInformation(parentNote: FNote) {
|
||||
const info: PromotedAttributeInformation[] = [];
|
||||
for (const promotedAttribute of parentNote.getPromotedDefinitionAttributes()) {
|
||||
const def = promotedAttribute.getDefinition();
|
||||
if (def.multiplicity !== "single") {
|
||||
console.warn("Multiple values are not supported for now");
|
||||
continue;
|
||||
}
|
||||
|
||||
const [ labelType, name ] = promotedAttribute.name.split(":", 2);
|
||||
if (promotedAttribute.type !== "label") {
|
||||
console.warn("Relations are not supported for now");
|
||||
continue;
|
||||
}
|
||||
|
||||
let type: LabelType | "relation" = def.labelType || "text";
|
||||
if (labelType === "relation") {
|
||||
type = "relation";
|
||||
}
|
||||
|
||||
info.push({
|
||||
name,
|
||||
title: def.promotedAlias,
|
||||
type
|
||||
});
|
||||
}
|
||||
console.log("Promoted attribute information", info);
|
||||
return info;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user