Merge pull request #9 from h-sphere/feat/chart-full-screen

feat: now each chart can be opened in full screen
This commit is contained in:
Kacper Kula 2025-08-14 12:45:51 +02:00 committed by GitHub
commit f359f0e587
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 171 additions and 8 deletions

View file

@ -0,0 +1,5 @@
---
"sqlseal-charts": patch
---
now every chart can be opened in full screen

View file

@ -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<string, any>) {
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

69
src/fullscreenModal.ts Normal file
View file

@ -0,0 +1,69 @@
import { App, Modal } from "obsidian";
import * as echarts from "echarts";
export class FullScreenChartModal extends Modal {
private chart: echarts.ECharts | null = null;
private chartConfig: Record<string, any>;
private resizeHandler: (() => void) | null = null;
constructor(app: App, chartConfig: Record<string, any>) {
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");
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.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;
}
if (this.chart) {
this.chart.dispose();
this.chart = null;
}
}
}

View file

@ -1,11 +1,81 @@
.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-charts-container:hover .sqlseal-chart-header {
opacity: 1;
}
.sqlseal-chart-header {
display: flex;
justify-content: flex-end;
position: absolute;
bottom: 8px;
right: 8px;
z-index: 10;
opacity: 0;
transition: opacity 0.2s ease;
}
.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;
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;
opacity: 0;
transition: opacity 0.2s ease;
}
.sqlseal-fullscreen-modal.loaded {
opacity: 1;
}
.sqlseal-fullscreen-modal .modal-content {
width: 95vw !important;
height: 90vh !important;
max-width: none !important;
max-height: none !important;
margin: 2.5vh 2.5vw !important;
position: absolute !important;
left: 2.5vw !important;
top: 5vh !important;
}
.sqlseal-fullscreen-chart-container {
width: 100%;
height: calc(90vh - 40px);
min-height: 400px;
}