From 22dbb66fcd52cc8639e888c142c9ba8c65ce8462 Mon Sep 17 00:00:00 2001 From: Kacper Kula Date: Wed, 13 Aug 2025 21:12:02 +0100 Subject: [PATCH 1/2] feat: now each chart can be opened in full screen --- .changeset/hungry-jokes-say.md | 5 ++ src/chartRenderer.ts | 29 ++++++++++-- src/fullscreenModal.ts | 87 ++++++++++++++++++++++++++++++++++ styles.css | 64 +++++++++++++++++++++++-- 4 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 .changeset/hungry-jokes-say.md create mode 100644 src/fullscreenModal.ts diff --git a/.changeset/hungry-jokes-say.md b/.changeset/hungry-jokes-say.md new file mode 100644 index 0000000..684f3e2 --- /dev/null +++ b/.changeset/hungry-jokes-say.md @@ -0,0 +1,5 @@ +--- +"sqlseal-charts": patch +--- + +now every chart can be opened in full screen diff --git a/src/chartRenderer.ts b/src/chartRenderer.ts index 9d1ae5f..6f915ee 100644 --- a/src/chartRenderer.ts +++ b/src/chartRenderer.ts @@ -1,4 +1,4 @@ -import { App } from "obsidian"; +import { App, setIcon } from "obsidian"; import { parseCode } from "./utils/configParser"; import { prepareDataVariables } from "./utils/prepareDataVariables"; import * as echarts from 'echarts'; @@ -6,6 +6,7 @@ import * as ecStat from 'echarts-stat'; import type { RendererConfig } from "@hypersphere/sqlseal"; import { ViewDefinition } from "@hypersphere/sqlseal/dist/src/grammar/parser"; import { parseCodeAdvanced } from "./utils/advancedParser"; +import { FullScreenChartModal } from "./fullscreenModal"; interface Config { config: string @@ -36,6 +37,20 @@ export class ChartRenderer implements RendererConfig { return { config: config.trim() } } + private createFullscreenButton(container: HTMLElement, chartConfig: Record) { + const fullscreenButton = container.createEl('button', { + cls: 'sqlseal-fullscreen-button', + attr: { 'aria-label': 'Open chart in fullscreen' } + }) + setIcon(fullscreenButton, 'maximize-2') + + fullscreenButton.addEventListener('click', (e) => { + e.stopPropagation(); + const modal = new FullScreenChartModal(this.app, chartConfig); + modal.open(); + }) + } + render(config: Config, el: HTMLElement) { let isRendered: boolean = false let chart: echarts.ECharts | null = null @@ -78,12 +93,16 @@ export class ChartRenderer implements RendererConfig { el.empty() const container = el.createDiv({ cls: 'sqlseal-charts-container' }) - const chartDiv = container.createDiv() + + const chartHeader = container.createDiv({ cls: 'sqlseal-chart-header' }) + const chartDiv = container.createDiv({ cls: 'sqlseal-chart-content' }) + + this.createFullscreenButton(chartHeader, configRecord) requestAnimationFrame(() => { - const box = container.getBoundingClientRect() - const width = box.width - const height = box.height + const containerBox = container.getBoundingClientRect() + const width = containerBox.width + const height = containerBox.height chart = echarts.init(chartDiv, null, { height: height, width: width }) chart.setOption(configRecord) isRendered = true diff --git a/src/fullscreenModal.ts b/src/fullscreenModal.ts new file mode 100644 index 0000000..9df3a39 --- /dev/null +++ b/src/fullscreenModal.ts @@ -0,0 +1,87 @@ +import { App, Modal } from "obsidian"; +import * as echarts from 'echarts'; + +export class FullScreenChartModal extends Modal { + private chart: echarts.ECharts | null = null; + private chartConfig: Record; + private resizeHandler: (() => void) | null = null; + + constructor(app: App, chartConfig: Record) { + super(app); + this.chartConfig = chartConfig; + + this.scope.register([], "Escape", () => { + this.close(); + }); + } + + onOpen() { + const { contentEl, modalEl } = this; + contentEl.empty(); + + // Apply fullscreen classes + modalEl.addClass('sqlseal-fullscreen-modal'); + contentEl.addClass('sqlseal-fullscreen-modal'); + + // Force modal to fullscreen dimensions + modalEl.style.width = '100vw'; + modalEl.style.height = '100vh'; + modalEl.style.maxWidth = 'none'; + modalEl.style.maxHeight = 'none'; + modalEl.style.left = '0'; + modalEl.style.top = '0'; + modalEl.style.transform = 'none'; + modalEl.style.margin = '0'; + + // Set content area dimensions + contentEl.style.width = '95vw'; + contentEl.style.height = '90vh'; + contentEl.style.maxWidth = 'none'; + contentEl.style.maxHeight = 'none'; + contentEl.style.margin = '2.5vh 2.5vw'; + contentEl.style.position = 'absolute'; + contentEl.style.left = '2.5vw'; + contentEl.style.top = '5vh'; + + const chartContainer = contentEl.createDiv({ cls: 'sqlseal-fullscreen-chart-container' }); + + // Initialize chart after modal is rendered + requestAnimationFrame(() => { + requestAnimationFrame(() => { + const containerRect = chartContainer.getBoundingClientRect(); + + const width = containerRect.width || window.innerWidth * 0.9; + const height = containerRect.height || window.innerHeight * 0.8; + + this.chart = echarts.init(chartContainer, null, { width, height }); + this.chart.setOption(this.chartConfig); + + this.resizeHandler = () => { + if (this.chart) { + this.chart.resize(); + } + }; + + window.addEventListener('resize', this.resizeHandler); + + // Ensure proper sizing after initialization + setTimeout(() => { + if (this.chart) { + this.chart.resize(); + } + }, 100); + }); + }); + } + + onClose() { + if (this.resizeHandler) { + window.removeEventListener('resize', this.resizeHandler); + this.resizeHandler = null; + } + if (this.chart) { + this.chart.dispose(); + this.chart = null; + } + } +} \ No newline at end of file diff --git a/styles.css b/styles.css index 7e497fa..8db2c18 100644 --- a/styles.css +++ b/styles.css @@ -1,11 +1,69 @@ .sqlseal-charts-container { - display: grid; - justify-content: center; + display: flex; + flex-direction: column; width: 100%; aspect-ratio: 16 / 9; + position: relative; } -.sqlseal-charts-container > div { +.sqlseal-chart-header { + display: flex; + justify-content: flex-end; + position: absolute; + bottom: 8px; + right: 8px; + z-index: 10; +} + +.sqlseal-fullscreen-button { + background: var(--background-modifier-hover); + border: none; + border-radius: var(--radius-s); + padding: 4px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + opacity: 0.7; + transition: opacity 0.2s ease; +} + +.sqlseal-fullscreen-button:hover { + background: var(--background-modifier-border-hover); + opacity: 1; +} + +.sqlseal-chart-content { width: 100%; height: 100%; + flex: 1; +} + +.sqlseal-fullscreen-modal { + z-index: 9999; +} + +.sqlseal-fullscreen-modal .modal { + width: 100vw !important; + height: 100vh !important; + max-width: none !important; + max-height: none !important; + margin: 0 !important; + padding: 0 !important; +} + +.sqlseal-fullscreen-modal .modal-content { + width: 95vw !important; + height: 90vh !important; + max-width: none !important; + max-height: none !important; + padding: 20px !important; + margin: 2.5vh 2.5vw !important; + border-radius: 8px !important; +} + +.sqlseal-fullscreen-chart-container { + width: 100%; + height: calc(90vh - 40px); + min-height: 400px; } \ No newline at end of file From 610cf9620b6c9ec436a2f202c30034ba5e36dd3e Mon Sep 17 00:00:00 2001 From: Kacper Kula Date: Thu, 14 Aug 2025 11:44:59 +0100 Subject: [PATCH 2/2] feat: improving chart modal, hiding button by default --- src/fullscreenModal.ts | 138 ++++++++++++++++++----------------------- styles.css | 24 +++++-- 2 files changed, 78 insertions(+), 84 deletions(-) diff --git a/src/fullscreenModal.ts b/src/fullscreenModal.ts index 9df3a39..39ad699 100644 --- a/src/fullscreenModal.ts +++ b/src/fullscreenModal.ts @@ -1,87 +1,69 @@ import { App, Modal } from "obsidian"; -import * as echarts from 'echarts'; +import * as echarts from "echarts"; export class FullScreenChartModal extends Modal { - private chart: echarts.ECharts | null = null; - private chartConfig: Record; - private resizeHandler: (() => void) | null = null; + private chart: echarts.ECharts | null = null; + private chartConfig: Record; + private resizeHandler: (() => void) | null = null; - constructor(app: App, chartConfig: Record) { - super(app); - this.chartConfig = chartConfig; - - this.scope.register([], "Escape", () => { - this.close(); - }); - } + constructor(app: App, chartConfig: Record) { + super(app); + this.chartConfig = chartConfig; - onOpen() { - const { contentEl, modalEl } = this; - contentEl.empty(); - - // Apply fullscreen classes - modalEl.addClass('sqlseal-fullscreen-modal'); - contentEl.addClass('sqlseal-fullscreen-modal'); - - // Force modal to fullscreen dimensions - modalEl.style.width = '100vw'; - modalEl.style.height = '100vh'; - modalEl.style.maxWidth = 'none'; - modalEl.style.maxHeight = 'none'; - modalEl.style.left = '0'; - modalEl.style.top = '0'; - modalEl.style.transform = 'none'; - modalEl.style.margin = '0'; - - // Set content area dimensions - contentEl.style.width = '95vw'; - contentEl.style.height = '90vh'; - contentEl.style.maxWidth = 'none'; - contentEl.style.maxHeight = 'none'; - contentEl.style.margin = '2.5vh 2.5vw'; - contentEl.style.position = 'absolute'; - contentEl.style.left = '2.5vw'; - contentEl.style.top = '5vh'; - - const chartContainer = contentEl.createDiv({ cls: 'sqlseal-fullscreen-chart-container' }); - - // Initialize chart after modal is rendered - requestAnimationFrame(() => { - requestAnimationFrame(() => { - const containerRect = chartContainer.getBoundingClientRect(); - - const width = containerRect.width || window.innerWidth * 0.9; - const height = containerRect.height || window.innerHeight * 0.8; - - this.chart = echarts.init(chartContainer, null, { width, height }); - this.chart.setOption(this.chartConfig); - - this.resizeHandler = () => { - if (this.chart) { - this.chart.resize(); - } - }; - - window.addEventListener('resize', this.resizeHandler); - - // Ensure proper sizing after initialization - setTimeout(() => { - if (this.chart) { - this.chart.resize(); - } - }, 100); - }); - }); - } + this.scope.register([], "Escape", () => { + this.close(); + }); + } - onClose() { - if (this.resizeHandler) { - window.removeEventListener('resize', this.resizeHandler); - this.resizeHandler = null; - } + onOpen() { + const { contentEl, modalEl } = this; + contentEl.empty(); + + // Apply fullscreen classes + modalEl.addClass("sqlseal-fullscreen-modal"); + + const chartContainer = contentEl.createDiv({ + cls: "sqlseal-fullscreen-chart-container", + }); + + // Initialize chart after modal is rendered + requestAnimationFrame(() => { + const containerRect = chartContainer.getBoundingClientRect(); + + const width = containerRect.width || window.innerWidth * 0.9; + const height = containerRect.height || window.innerHeight * 0.8; + + this.chart = echarts.init(chartContainer, null, { width, height }); + this.chart.setOption(this.chartConfig); + + this.resizeHandler = () => { if (this.chart) { - this.chart.dispose(); - this.chart = null; + this.chart.resize(); } + }; + + window.addEventListener("resize", this.resizeHandler); + + // Show modal after chart is loaded + modalEl.addClass("loaded"); + + // Ensure proper sizing after initialization + setTimeout(() => { + if (this.chart) { + this.chart.resize(); + } + }, 100); + }); + } + + onClose() { + if (this.resizeHandler) { + window.removeEventListener("resize", this.resizeHandler); + this.resizeHandler = null; } -} \ No newline at end of file + if (this.chart) { + this.chart.dispose(); + this.chart = null; + } + } +} diff --git a/styles.css b/styles.css index 8db2c18..c3de046 100644 --- a/styles.css +++ b/styles.css @@ -6,6 +6,10 @@ position: relative; } +.sqlseal-charts-container:hover .sqlseal-chart-header { + opacity: 1; +} + .sqlseal-chart-header { display: flex; justify-content: flex-end; @@ -13,6 +17,8 @@ bottom: 8px; right: 8px; z-index: 10; + opacity: 0; + transition: opacity 0.2s ease; } .sqlseal-fullscreen-button { @@ -41,15 +47,20 @@ .sqlseal-fullscreen-modal { z-index: 9999; -} - -.sqlseal-fullscreen-modal .modal { width: 100vw !important; height: 100vh !important; max-width: none !important; max-height: none !important; + left: 0 !important; + top: 0 !important; + transform: none !important; margin: 0 !important; - padding: 0 !important; + opacity: 0; + transition: opacity 0.2s ease; +} + +.sqlseal-fullscreen-modal.loaded { + opacity: 1; } .sqlseal-fullscreen-modal .modal-content { @@ -57,9 +68,10 @@ height: 90vh !important; max-width: none !important; max-height: none !important; - padding: 20px !important; margin: 2.5vh 2.5vw !important; - border-radius: 8px !important; + position: absolute !important; + left: 2.5vw !important; + top: 5vh !important; } .sqlseal-fullscreen-chart-container {