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 froca from "../../../services/froca.js";
|
||||||
import ViewMode, { type ViewModeArgs } from "../view_mode.js";
|
import ViewMode, { type ViewModeArgs } from "../view_mode.js";
|
||||||
import attributes, { setAttribute, setLabel } from "../../../services/attributes.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 server from "../../../services/server.js";
|
||||||
import SpacedUpdate from "../../../services/spaced_update.js";
|
import SpacedUpdate from "../../../services/spaced_update.js";
|
||||||
import type { CommandListenerData, EventData } from "../../../components/app_context.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 "tabulator-tables/dist/css/tabulator_bootstrap5.min.css";
|
||||||
import { canReorderRows, configureReorderingRows } from "./dragging.js";
|
import { canReorderRows, configureReorderingRows } from "./dragging.js";
|
||||||
import buildFooter from "./footer.js";
|
import buildFooter from "./footer.js";
|
||||||
|
import getPromotedAttributeInformation, { buildRowDefinitions } from "./rows.js";
|
||||||
|
import { buildColumnDefinitions } from "./columns.js";
|
||||||
|
|
||||||
const TPL = /*html*/`
|
const TPL = /*html*/`
|
||||||
<div class="table-view">
|
<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