From af296a1e4e05ff9d33c13460a093f22964314bb0 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 4 Jul 2025 21:18:52 +0300 Subject: [PATCH] refactor(views/table): split column & rows into separate file --- .../view_widgets/table_view/columns.ts | 110 ++++++++++ .../widgets/view_widgets/table_view/data.ts | 194 ------------------ .../widgets/view_widgets/table_view/index.ts | 3 +- .../widgets/view_widgets/table_view/rows.ts | 74 +++++++ 4 files changed, 186 insertions(+), 195 deletions(-) create mode 100644 apps/client/src/widgets/view_widgets/table_view/columns.ts delete mode 100644 apps/client/src/widgets/view_widgets/table_view/data.ts create mode 100644 apps/client/src/widgets/view_widgets/table_view/rows.ts diff --git a/apps/client/src/widgets/view_widgets/table_view/columns.ts b/apps/client/src/widgets/view_widgets/table_view/columns.ts new file mode 100644 index 000000000..8c0589928 --- /dev/null +++ b/apps/client/src/widgets/view_widgets/table_view/columns.ts @@ -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> = { + 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(); + 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; + 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; + } +} diff --git a/apps/client/src/widgets/view_widgets/table_view/data.ts b/apps/client/src/widgets/view_widgets/table_view/data.ts deleted file mode 100644 index 470d9b5b4..000000000 --- a/apps/client/src/widgets/view_widgets/table_view/data.ts +++ /dev/null @@ -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; - relations: Record; - branchId: string; -}; - -type ColumnType = LabelType | "relation"; - -export interface PromotedAttributeInformation { - name: string; - title?: string; - type?: ColumnType; -} - -const labelTypeMappings: Record> = { - 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(); - 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; - 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; -} diff --git a/apps/client/src/widgets/view_widgets/table_view/index.ts b/apps/client/src/widgets/view_widgets/table_view/index.ts index 88a7a89bd..c5a0963aa 100644 --- a/apps/client/src/widgets/view_widgets/table_view/index.ts +++ b/apps/client/src/widgets/view_widgets/table_view/index.ts @@ -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*/`
diff --git a/apps/client/src/widgets/view_widgets/table_view/rows.ts b/apps/client/src/widgets/view_widgets/table_view/rows.ts new file mode 100644 index 000000000..8263838bc --- /dev/null +++ b/apps/client/src/widgets/view_widgets/table_view/rows.ts @@ -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; + relations: Record; + 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; +}