mirror of
https://github.com/h-sphere/sql-seal.git
synced 2026-07-22 10:10:28 +00:00
chore: refactoring renderers and adding markdown text renderers
This commit is contained in:
parent
0447368f53
commit
053059a661
14 changed files with 110 additions and 79 deletions
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Args> {
|
|||
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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ export class CsvFileSyncStrategy extends ISyncStrategy {
|
|||
// TODO: PROBABLY SHOULD BE EXTRACTED SOMEWHERE FROM HERE later.
|
||||
const parsed = parse<Record<string, string>>(data, {
|
||||
header: true,
|
||||
dynamicTyping: true,
|
||||
dynamicTyping: false,
|
||||
skipEmptyLines: true,
|
||||
transformHeader: sanitise
|
||||
})
|
||||
|
|
|
|||
10
src/main.ts
10
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 () => {
|
||||
|
|
|
|||
|
|
@ -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<SQLSealApi> = []
|
||||
|
|
|
|||
|
|
@ -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<any>
|
||||
private _gridApi: GridApi<any>
|
||||
private errorEl: HTMLElement
|
||||
private errorOverlay: HTMLElement
|
||||
|
||||
get gridApi(): GridApi<any> {
|
||||
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<GridOptions>, el: HTMLElement) {
|
||||
const communicator = new GridRendererCommunicator(el, config, this.plugin, this.app, this.cellParser)
|
||||
render(config: Partial<GridOptions>, 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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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<string, any>[]) => {
|
|||
}
|
||||
|
||||
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<typeof this.validateConfig>, el: HTMLElement) {
|
||||
render(config: ReturnType<typeof this.validateConfig>, 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))
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -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 })
|
||||
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<T extends Record<string, any> = Record<string, any>> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in a new issue