diff --git a/src/cellParser/ModernCellParser.ts b/src/cellParser/ModernCellParser.ts index 43e45d4..22ad288 100644 --- a/src/cellParser/ModernCellParser.ts +++ b/src/cellParser/ModernCellParser.ts @@ -1,7 +1,7 @@ import { CellFunction } from "./CellFunction"; import { parse } from 'json5' import { isStringifiedArray, renderStringifiedArray } from '../utils/ui' -import { SqlSealDatabase } from "src/database/database"; +import { SqlSealDatabase } from "../database/database"; export type UnregisterCallback = () => void; @@ -34,13 +34,39 @@ export class ModernCellParser { // If it's an array, decode it and render each individual elements if (isStringifiedArray(content)) { try { - return renderStringifiedArray(content) + const parsed: string[] = parse(content) + const container = createSpan() + parsed.forEach((p, i) => { + const rendered = this.render(p) + if (rendered instanceof HTMLElement) { + container.appendChild(rendered) + if (i < parsed.length -1) { + container.appendChild(document.createTextNode(', ')) + } + } + }) + return container } catch (e) { return content } } - // If it is link string, use "a" renderer. + // If it is a wikilink or markdown link, use "a" renderer + if (content.startsWith('[[') && content.endsWith(']]')) { + const renderer = this.functions.get('a'); + if (renderer) { + const [link, name] = content.slice(2, -2).split('|'); + return renderer.prepare([link, name]); + } + } else if (content.startsWith('[') && content.includes('](') && content.endsWith(')')) { + const renderer = this.functions.get('a'); + if (renderer) { + const closingBracketIndex = content.indexOf(']('); + const name = content.slice(1, closingBracketIndex); + const href = content.slice(closingBracketIndex + 2, -1); + return renderer.prepare([href, name]); + } + } return content } catch (e) { @@ -66,7 +92,7 @@ export class ModernCellParser { } } - renderAsString(content: string) { + renderAsString(content: string): string { try { if (!content) { return content @@ -85,10 +111,27 @@ export class ModernCellParser { if (isStringifiedArray(content)) { try { const parsed: string[] = parse(content) - return parsed.map(p => this.renderer) - return renderStringifiedArray(content) + return parsed.map(p => this.renderAsString(p)).join(', ') } catch (e) { - return content + return content.toString() + } + } + + + // If it is a wikilink or markdown link, use "a" renderer + if (content.startsWith('[[') && content.endsWith(']]')) { + const renderer = this.functions.get('a'); + if (renderer) { + const [link, name] = content.slice(2, -2).split('|'); + return renderer.renderAsString([link, name]); + } + } else if (content.startsWith('[') && content.includes('](') && content.endsWith(')')) { + const renderer = this.functions.get('a'); + if (renderer) { + const closingBracketIndex = content.indexOf(']('); + const name = content.slice(1, closingBracketIndex); + const href = content.slice(closingBracketIndex + 2, -1); + return renderer.renderAsString([href, name]); } } diff --git a/src/cellParser/parser/image.ts b/src/cellParser/parser/image.ts index 38abd17..e201864 100644 --- a/src/cellParser/parser/image.ts +++ b/src/cellParser/parser/image.ts @@ -1,7 +1,7 @@ +import { isLinkLocal } from "../../utils/ui/helperFunctions"; import { CellFunction } from "../CellFunction"; import { CellParserResult } from "../ModernCellParser"; import { App } from "obsidian"; -import { isLinkLocal } from "src/utils/ui/helperFunctions"; type Args = [string] | [string, string] diff --git a/src/cellParser/parser/link.ts b/src/cellParser/parser/link.ts index 541cca1..2e5745e 100644 --- a/src/cellParser/parser/link.ts +++ b/src/cellParser/parser/link.ts @@ -1,9 +1,7 @@ import { isStringifiedArray, renderStringifiedArray } from "../../utils/ui"; +import { isLinkLocal, isLinktext, removeExtension } from "../../utils/ui/helperFunctions"; import { CellFunction } from "../CellFunction"; -import { CellParserResult, Result } from "../ModernCellParser"; import { App } from "obsidian"; -import { isLinkLocal, isLinktext, removeExtension } from "src/utils/ui/helperFunctions"; -import { parse } from 'json5' type Args = [string] | [string, string] @@ -79,24 +77,6 @@ export class LinkParser implements CellFunction { if (!href) { return '' } - - // if (isStringifiedArray(href)) { - // try { - // // FIXME: parse and run render as strings - // const arr: string[] = parse(href) - - // return arr.map(e => this.renderAsString(e)) - - // const res = renderStringifiedArray(href, href => this.prepare([href])) - // if (res instanceof HTMLElement) { - // return res.outerHTML - // } - // return res - // } catch (e) { - // return href - // } - // } - const res = this.parseLink(href, name) if (res.cls == 'internal-link') { if (!name) { diff --git a/src/codeblockHandler/CodeblockProcessor.ts b/src/codeblockHandler/CodeblockProcessor.ts index 5b76cea..8b3bc01 100644 --- a/src/codeblockHandler/CodeblockProcessor.ts +++ b/src/codeblockHandler/CodeblockProcessor.ts @@ -69,7 +69,12 @@ export class CodeblockProcessor extends MarkdownRenderChild { rendererEl = this.el.createDiv({ cls: 'sqlseal-renderer-container' }) } - this.renderer = this.rendererRegistry.prepareRender(results.renderer.type.toLowerCase(), results.renderer.options)(rendererEl) + this.renderer = this.rendererRegistry + .prepareRender( + results.renderer.type.toLowerCase(), results.renderer.options + )(rendererEl, { + cellParser: this.plugin.cellParser + }) // FIXME: probably should save the one before transform and perform transform every time we execute it. this.query = results.query diff --git a/src/datamodel/syncStrategy/CsvFileSyncStrategy.ts b/src/datamodel/syncStrategy/CsvFileSyncStrategy.ts index 7439e57..baf00a4 100644 --- a/src/datamodel/syncStrategy/CsvFileSyncStrategy.ts +++ b/src/datamodel/syncStrategy/CsvFileSyncStrategy.ts @@ -56,7 +56,7 @@ export class CsvFileSyncStrategy extends ISyncStrategy { // TODO: PROBABLY SHOULD BE EXTRACTED SOMEWHERE FROM HERE later. const parsed = parse>(data, { header: true, - dynamicTyping: true, + dynamicTyping: false, skipEmptyLines: true, transformHeader: sanitise }) diff --git a/src/main.ts b/src/main.ts index ba31960..20792a6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -58,11 +58,11 @@ export default class SqlSealPlugin extends Plugin { // REGISTERING VIEWS - this.rendererRegistry.register('sql-seal-internal-table', new TableRenderer(this.app, this.cellParser)) - this.rendererRegistry.register('sql-seal-internal-grid', new GridRenderer(this.app, this, this.cellParser)) - this.rendererRegistry.register('sql-seal-internal-markdown', new MarkdownRenderer(this.app, this.cellParser)) - this.rendererRegistry.register('sql-seal-internal-list', new ListRenderer(this.app, this.cellParser)) - this.rendererRegistry.register('sql-seal-internal-template', new TemplateRenderer(this.app, this.cellParser)) + this.rendererRegistry.register('sql-seal-internal-table', new TableRenderer(this.app)) + this.rendererRegistry.register('sql-seal-internal-grid', new GridRenderer(this.app, this)) + this.rendererRegistry.register('sql-seal-internal-markdown', new MarkdownRenderer(this.app)) + this.rendererRegistry.register('sql-seal-internal-list', new ListRenderer(this.app)) + this.rendererRegistry.register('sql-seal-internal-template', new TemplateRenderer(this.app)) // start syncing when files are loaded this.app.workspace.onLayoutReady(async () => { diff --git a/src/pluginApi/sqlSealApi.ts b/src/pluginApi/sqlSealApi.ts index f159c53..47347b0 100644 --- a/src/pluginApi/sqlSealApi.ts +++ b/src/pluginApi/sqlSealApi.ts @@ -4,7 +4,7 @@ import { version } from '../../package.json' import { RendererConfig } from "../renderer/rendererRegistry"; import { CellFunction } from "../cellParser/CellFunction"; -const API_VERSION = 3; +const API_VERSION = 4; export class SQLSealRegisterApi { registeredApis: Array = [] diff --git a/src/renderer/GridRenderer.ts b/src/renderer/GridRenderer.ts index 2983331..aa34120 100644 --- a/src/renderer/GridRenderer.ts +++ b/src/renderer/GridRenderer.ts @@ -1,11 +1,11 @@ import { createGrid, GridApi, GridOptions, themeQuartz } from "ag-grid-community"; import { merge } from "lodash"; import { App } from "obsidian"; -import { RendererConfig } from "./rendererRegistry"; +import { RendererConfig, RendererContext } from "./rendererRegistry"; import { parse } from 'json5' import { ViewDefinition } from "../grammar/parser"; import SqlSealPlugin from "../main"; -import { ModernCellParser } from "src/cellParser/ModernCellParser"; +import { ModernCellParser } from "../cellParser/ModernCellParser"; const getCurrentTheme = () => { return document.body.classList.contains('theme-dark') ? 'dark' : 'light'; @@ -36,10 +36,14 @@ class GridRendererCommunicator { this.initialize() } - private gridApi: GridApi + private _gridApi: GridApi private errorEl: HTMLElement private errorOverlay: HTMLElement + get gridApi(): GridApi { + return this._gridApi + } + private showError(message: string) { this.gridApi.setGridOption('loading', false) this.errorEl.textContent = message //.replace(`TTT${prefix}_`, ''); @@ -76,8 +80,9 @@ class GridRendererCommunicator { autoHeight: true }, autoSizeStrategy: { + // make sure to fit content type: 'fitGridWidth', - defaultMinWidth: 150, + // defaultMinWidth: 150, }, pagination: true, suppressMovableColumns: true, @@ -89,7 +94,7 @@ class GridRendererCommunicator { paginationPageSize: this.plugin? this.plugin.settings.gridItemsPerPage : undefined, // ensureDomOrder: true }, this.config) - this.gridApi = createGrid( + this._gridApi = createGrid( grid, gridOptions, ); @@ -118,7 +123,7 @@ class GridRendererCommunicator { } export class GridRenderer implements RendererConfig { - constructor(private app: App, private readonly plugin: SqlSealPlugin | null, private readonly cellParser: ModernCellParser) { } + constructor(private app: App, private readonly plugin: SqlSealPlugin | null) { } get viewDefinition(): ViewDefinition { return { name: this.rendererKey, @@ -140,12 +145,13 @@ export class GridRenderer implements RendererConfig { } - render(config: Partial, el: HTMLElement) { - const communicator = new GridRendererCommunicator(el, config, this.plugin, this.app, this.cellParser) + render(config: Partial, el: HTMLElement, { cellParser }: RendererContext) { + const communicator = new GridRendererCommunicator(el, config, this.plugin, this.app, cellParser) return { render: (data: any) => { - // FIXME: we need to update that. communicator.setData(data.columns, data.data) + communicator.gridApi.autoSizeAllColumns() + }, error: (message: string) => { communicator.showInfo('error', message) diff --git a/src/renderer/ListRenderer.ts b/src/renderer/ListRenderer.ts index 792a13c..f9433a4 100644 --- a/src/renderer/ListRenderer.ts +++ b/src/renderer/ListRenderer.ts @@ -1,9 +1,8 @@ // This is renderer for a very basic List view. import { App } from "obsidian"; -import { RendererConfig } from "../renderer/rendererRegistry"; +import { RendererConfig, RendererContext } from "../renderer/rendererRegistry"; import { displayError } from "../utils/ui"; import { ViewDefinition } from "../grammar/parser"; -import { ModernCellParser } from "src/cellParser/ModernCellParser"; interface ListRendererConfig { classNames: string[] @@ -11,7 +10,7 @@ interface ListRendererConfig { export class ListRenderer implements RendererConfig { - constructor(private readonly app: App, private readonly cellParser: ModernCellParser) { } + constructor(private readonly app: App) { } get rendererKey() { return 'list' @@ -37,7 +36,7 @@ export class ListRenderer implements RendererConfig { } } - render(config: ListRendererConfig, el: HTMLElement) { + render(config: ListRendererConfig, el: HTMLElement, { cellParser }: RendererContext) { return { render: ({ columns, data }: any) => { el.empty() @@ -58,7 +57,7 @@ export class ListRenderer implements RendererConfig { text: createEl('span', { text: c, cls: 'sqlseal-column-name' }) as any, // FIXME: this should be properly typed cls: singleCol ? ['sqlseal-list-element', 'sqlseal-list-element-single'] : ['sqlseal-list-element-single'] }) - const val = this.cellParser.render(d[c]) + const val = cellParser.render(d[c]) el.append(val) el.dataset.sqlsealColumn = c }) diff --git a/src/renderer/MarkdownRenderer.ts b/src/renderer/MarkdownRenderer.ts index 3a93ee8..a0bccb0 100644 --- a/src/renderer/MarkdownRenderer.ts +++ b/src/renderer/MarkdownRenderer.ts @@ -1,7 +1,7 @@ // This is renderer for a very basic Table view. import { getMarkdownTable } from "markdown-table-ts"; import { App } from "obsidian"; -import { RendererConfig } from "../renderer/rendererRegistry"; +import { RendererConfig, RendererContext } from "../renderer/rendererRegistry"; import { displayError } from "../utils/ui"; import { ViewDefinition } from "../grammar/parser"; import { ParseResults } from "../cellParser/parseResults"; @@ -12,12 +12,7 @@ const mapDataFromHeaders = (columns: string[], data: Record[]) => { } export class MarkdownRenderer implements RendererConfig { - - parseResult: ParseResults; - - constructor(private readonly app: App, private readonly cellParser: ModernCellParser) { - this.parseResult = new ParseResults(cellParser) - + constructor(private readonly app: App) { } get rendererKey() { @@ -35,13 +30,14 @@ export class MarkdownRenderer implements RendererConfig { } } - render(config: ReturnType, el: HTMLElement) { + render(config: ReturnType, el: HTMLElement, { cellParser } : RendererContext) { + const parseResult = new ParseResults(cellParser) return { render: ({ columns, data }: any) => { const tab = getMarkdownTable({ table: { head: columns, - body: mapDataFromHeaders(columns, this.parseResult.renderAsString(data, columns)) + body: mapDataFromHeaders(columns, parseResult.renderAsString(data, columns)) } }) diff --git a/src/renderer/TableRenderer.ts b/src/renderer/TableRenderer.ts index 722ed96..59bc492 100644 --- a/src/renderer/TableRenderer.ts +++ b/src/renderer/TableRenderer.ts @@ -1,6 +1,6 @@ // This is renderer for a very basic Table view. import { App } from "obsidian"; -import { RendererConfig } from "../renderer/rendererRegistry"; +import { RendererConfig, RendererContext } from "../renderer/rendererRegistry"; import { displayError } from "../utils/ui"; import { ViewDefinition } from "../grammar/parser"; import { ModernCellParser } from "../cellParser/ModernCellParser"; @@ -11,7 +11,7 @@ interface HTMLRendererConfig { export class TableRenderer implements RendererConfig { - constructor(private readonly app: App, private cellParser: ModernCellParser) { } + constructor(private readonly app: App) { } get rendererKey() { return 'html' @@ -36,7 +36,7 @@ export class TableRenderer implements RendererConfig { } } - render(config: HTMLRendererConfig, el: HTMLElement) { + render(config: HTMLRendererConfig, el: HTMLElement, { cellParser }: RendererContext) { return { render: ({ columns, data }: any) => { el.empty() @@ -60,7 +60,7 @@ export class TableRenderer implements RendererConfig { data.forEach((d: any) => { const row = body.createEl("tr") columns.forEach((c: any) => { - row.createEl("td", { text: this.cellParser.render(d[c]) as string }) + row.createEl("td", { text: cellParser.render(d[c]) as string }) }) }) diff --git a/src/renderer/TemplateRenderer.ts b/src/renderer/TemplateRenderer.ts index a0e9209..70286fd 100644 --- a/src/renderer/TemplateRenderer.ts +++ b/src/renderer/TemplateRenderer.ts @@ -1,22 +1,17 @@ // This is renderer for a very basic List view. import { App } from "obsidian"; -import { RendererConfig } from "./rendererRegistry"; +import { RendererConfig, RendererContext } from "./rendererRegistry"; import { displayError } from "../utils/ui"; import { ViewDefinition } from "../grammar/parser"; import Handlebars from "handlebars"; import { ParseResults } from "../cellParser/parseResults"; -import { ModernCellParser } from "../cellParser/ModernCellParser"; interface TemplateRendererConfig { template: HandlebarsTemplateDelegate } export class TemplateRenderer implements RendererConfig { - - parseResult: ParseResults; - - constructor(private readonly app: App, private readonly cellParser: ModernCellParser) { - this.parseResult = new ParseResults(cellParser, (el) => new Handlebars.SafeString(el.outerHTML)) + constructor(private readonly app: App) { } get rendererKey() { @@ -42,18 +37,20 @@ export class TemplateRenderer implements RendererConfig { } } - render(config: TemplateRendererConfig, el: HTMLElement) { + render(config: TemplateRendererConfig, el: HTMLElement, { cellParser }: RendererContext) { return { render: ({ columns, data, frontmatter }: any) => { el.empty() + const parser = new ParseResults(cellParser, (el) => new Handlebars.SafeString(el.outerHTML)) + // Seems to be the only way to render handlebars into DOM. Don't like it but what can we do. el.innerHTML = config.template({ - data: this.parseResult.parse(data, columns), + data: parser.parse(data, columns), columns, properties: frontmatter }) - this.parseResult.initialise(el) + parser.initialise(el) }, error: (error: string) => { displayError(el, error) diff --git a/src/renderer/rendererRegistry.ts b/src/renderer/rendererRegistry.ts index 2512e70..e254d68 100644 --- a/src/renderer/rendererRegistry.ts +++ b/src/renderer/rendererRegistry.ts @@ -1,3 +1,4 @@ +import { ModernCellParser } from "../cellParser/ModernCellParser"; import { ViewDefinition } from "../grammar/parser"; export interface DataFormat { @@ -5,6 +6,10 @@ export interface DataFormat { columns: string[] } +export interface RendererContext { + cellParser: ModernCellParser +} + export interface RenderReturn { render: (data: any) => void; error: (errorMessage: string) => void; @@ -12,7 +17,7 @@ export interface RenderReturn { export interface RendererConfig = Record> { rendererKey: string; validateConfig: (config: string) => T, - render: (config: T, el: HTMLElement) => RenderReturn, + render: (config: T, el: HTMLElement, context: RendererContext) => RenderReturn, viewDefinition: ViewDefinition } @@ -66,8 +71,8 @@ export class RendererRegistry { } const rendererConfig = this.renderersByKey.get(type.toLowerCase())! const elConfig = rendererConfig.validateConfig(config) - return (el: HTMLElement) => { - return rendererConfig.render(elConfig, el) + return (el: HTMLElement, context: RendererContext) => { + return rendererConfig.render(elConfig, el, context) } } diff --git a/src/view/CSVView.ts b/src/view/CSVView.ts index 19067b9..384dfb6 100644 --- a/src/view/CSVView.ts +++ b/src/view/CSVView.ts @@ -201,7 +201,7 @@ export class CSVView extends TextFileView { }) - const grid = new GridRenderer(this.app, null, this.cellParser) + const grid = new GridRenderer(this.app, null) const csvView = this; const api = grid.render({ defaultColDef: { @@ -276,7 +276,7 @@ export class CSVView extends TextFileView { }) menu.showAtMouseEvent(e.event as any) } - }, gridEl) + }, gridEl, { cellParser: this.cellParser }) this.api = api; this.loadDataIntoGrid()