From ae49aee79a46c6c79d7374c07619cb4e240cbf3b Mon Sep 17 00:00:00 2001 From: Ilfat Galiev Date: Fri, 29 Aug 2025 15:27:01 -0800 Subject: [PATCH 1/3] Arrow begin end marks sizes --- src/app/renderer/index.tsx | 40 +++++++++++++-------------- src/app/stores/migrator.ts | 2 ++ src/app/views/panels/link_creator.tsx | 6 ++++ src/core/graphics/elements.ts | 11 ++++++++ src/core/prototypes/links/index.ts | 35 +++++++++++++++++++++++ src/strings.ts | 2 ++ 6 files changed, 76 insertions(+), 20 deletions(-) diff --git a/src/app/renderer/index.tsx b/src/app/renderer/index.tsx index ff1fd8a6a..02df19be7 100644 --- a/src/app/renderer/index.tsx +++ b/src/app/renderer/index.tsx @@ -236,7 +236,7 @@ function renderEndSVGArrow(element: Graphics.Path) { arrowElement = ( {arrowElement} @@ -298,7 +298,7 @@ function renderStartSVGArrow(element: Graphics.Path) { arrowElement = ( {arrowElement} diff --git a/src/app/stores/migrator.ts b/src/app/stores/migrator.ts index 6ff6941eb..bf37ce9f9 100644 --- a/src/app/stores/migrator.ts +++ b/src/app/stores/migrator.ts @@ -1189,6 +1189,8 @@ export class Migrator { if (link) { link.properties.beginArrowType = ArrowType.NO_ARROW; link.properties.endArrowType = ArrowType.NO_ARROW; + link.properties.beginArrowSize = 10; + link.properties.endArrowSize = 10; } } } diff --git a/src/app/views/panels/link_creator.tsx b/src/app/views/panels/link_creator.tsx index 809d6463f..bb9619a97 100644 --- a/src/app/views/panels/link_creator.tsx +++ b/src/app/views/panels/link_creator.tsx @@ -567,6 +567,8 @@ export class LinkCreationPanel extends ContextedComponent< closeLink: false, beginArrowType: ArrowType.NO_ARROW, endArrowType: ArrowType.NO_ARROW, + beginArrowSize: 10, + endArrowSize: 10, } as Prototypes.Links.LinksProperties, }; @@ -614,6 +616,8 @@ export class LinkCreationPanel extends ContextedComponent< closeLink: false, beginArrowType: ArrowType.NO_ARROW, endArrowType: ArrowType.NO_ARROW, + beginArrowSize: 10, + endArrowSize: 10, } as Prototypes.Links.LinksProperties, }; @@ -665,6 +669,8 @@ export class LinkCreationPanel extends ContextedComponent< closeLink: false, beginArrowType: ArrowType.NO_ARROW, endArrowType: ArrowType.NO_ARROW, + beginArrowSize: 10, + endArrowSize: 10, } as Prototypes.Links.LinksProperties, }; diff --git a/src/core/graphics/elements.ts b/src/core/graphics/elements.ts index b25f31128..58b4f9581 100644 --- a/src/core/graphics/elements.ts +++ b/src/core/graphics/elements.ts @@ -158,6 +158,8 @@ export interface Path extends Element { transform: string; beginArrowType?: ArrowType; endArrowType?: ArrowType; + beginArrowSize?: number; + endArrowSize?: number; } export interface Circle extends Element { @@ -471,6 +473,15 @@ export class PathMaker { public setBeginArrowType(arrowType: ArrowType) { this.path.beginArrowType = arrowType; } + + public setBeginArrowSize(size: number) { + this.path.beginArrowSize = size; + } + + public setEndArrowSize(size: number) { + this.path.endArrowSize = size; + } + public setEndArrowType(arrowType: ArrowType) { this.path.endArrowType = arrowType; } diff --git a/src/core/prototypes/links/index.ts b/src/core/prototypes/links/index.ts index 128364a99..7baf59b0e 100644 --- a/src/core/prototypes/links/index.ts +++ b/src/core/prototypes/links/index.ts @@ -72,6 +72,8 @@ export interface LinksProperties extends Specification.AttributeMap { beginArrowType?: ArrowType; endArrowType?: ArrowType; + beginArrowSize?: number; + endArrowSize?: number; } export interface LinksAttributes extends Specification.AttributeMap { @@ -574,6 +576,8 @@ export abstract class LinksClass extends ChartElementClass { const lines: Graphics.Element[] = []; @@ -593,6 +597,8 @@ export abstract class LinksClass extends ChartElementClass Date: Fri, 29 Aug 2025 20:31:47 -0800 Subject: [PATCH 2/3] Refactoring, formatting --- src/app/actions/actions.ts | 1 - src/app/renderer/index.tsx | 118 ++++++++++----------- src/app/stores/action_handlers/document.ts | 21 ++-- src/app/stores/action_handlers/exports.ts | 12 --- src/app/stores/app_store.ts | 38 ++----- src/app/views/canvas/chart_display.tsx | 9 ++ src/app/views/file_view/export_view.tsx | 7 +- src/app/views/file_view/import_view.tsx | 4 +- src/app/views/file_view/index.tsx | 3 +- 9 files changed, 98 insertions(+), 115 deletions(-) delete mode 100644 src/app/stores/action_handlers/exports.ts diff --git a/src/app/actions/actions.ts b/src/app/actions/actions.ts index 8624fc028..e8859399b 100644 --- a/src/app/actions/actions.ts +++ b/src/app/actions/actions.ts @@ -78,7 +78,6 @@ export class ExportTemplate extends Action { export class SaveExportTemplatePropertyName extends Action { constructor( - public objectId: string, public propertyName: string, public value: string ) { diff --git a/src/app/renderer/index.tsx b/src/app/renderer/index.tsx index 02df19be7..c3b4ec2f9 100644 --- a/src/app/renderer/index.tsx +++ b/src/app/renderer/index.tsx @@ -215,8 +215,8 @@ class TextOnPath extends React.PureComponent<{ this.props.align == "start" ? "0%" : this.props.align == "middle" - ? "50%" - : "100%" + ? "50%" + : "100%" } > {this.props.text} @@ -236,7 +236,7 @@ function renderEndSVGArrow(element: Graphics.Path) { arrowElement = ( {rect.style.fillColor == null && - rect.style.fillStartColor && - rect.style.fillStopColor ? ( + rect.style.fillStartColor && + rect.style.fillStopColor ? ( {ellipse.style.fillColor == null && - ellipse.style.fillStartColor && - ellipse.style.fillStopColor ? ( + ellipse.style.fillStartColor && + ellipse.style.fillStopColor ? ( { - path.style.fillStartColor && - path.style.fillStopColor ? ( - - - - - ) : null} + path.style.fillStartColor && + path.style.fillStopColor ? ( + + + + + ) : null} { - // Get parent row indices from component row indices - const parentRowIndices = rowIndices.map( - (x) => component.selectable.rowIndices[x] - ); - // Query the selection with parent row indices - return options.selection.isSelected( - component.selectable.plotSegment.table, - parentRowIndices - ); - }, - } + isSelected: (table: string, rowIndices: number[]) => { + // Get parent row indices from component row indices + const parentRowIndices = rowIndices.map( + (x) => component.selectable.rowIndices[x] + ); + // Query the selection with parent row indices + return options.selection.isSelected( + component.selectable.plotSegment.table, + parentRowIndices + ); + }, + } : null; const convertEventHandler = ( diff --git a/src/app/stores/action_handlers/document.ts b/src/app/stores/action_handlers/document.ts index b9aab856a..776978c21 100644 --- a/src/app/stores/action_handlers/document.ts +++ b/src/app/stores/action_handlers/document.ts @@ -22,6 +22,7 @@ import { } from "../../application"; import { ChartTemplate, Dataset, Specification } from "../../../container"; import { TableType } from "../../../core/dataset"; +import { renderLocalSVG } from "../../views/canvas/chart_display"; declare let CHARTICULATOR_PACKAGE: { version: string; @@ -32,16 +33,19 @@ declare let CHARTICULATOR_PACKAGE: { /** Handlers for document-level actions such as Load, Save, Import, Export, Undo/Redo, Reset */ // eslint-disable-next-line export default function (REG: ActionHandlerRegistry) { + REG.add(Actions.SaveExportTemplatePropertyName, function (action) { + this.setPropertyExportName(action.propertyName, action.value); + }); + // eslint-disable-next-line REG.add(Actions.Export, function (action) { (async () => { // Export as vector graphics if (action.type == "svg") { - const svg = await this.renderLocalSVG(); + const svg = await renderLocalSVG(); const blob = new Blob([svg], { type: "image/svg;charset=utf-8" }); if (this.onExportTemplateCallback != null) { - if (this.onExportTemplateCallback(action.type, blob)) - { + if (this.onExportTemplateCallback(action.type, blob)) { return; } } @@ -51,7 +55,7 @@ export default function (REG: ActionHandlerRegistry) { if (action.type == "png" || action.type == "jpeg") { const svgDataURL = stringToDataURL( "image/svg+xml", - await this.renderLocalSVG() + await renderLocalSVG() ); renderDataURLToPNG(svgDataURL, { mode: "scale", @@ -60,8 +64,7 @@ export default function (REG: ActionHandlerRegistry) { }).then((png) => { png.toBlob((blob) => { if (this.onExportTemplateCallback != null) { - if (this.onExportTemplateCallback(action.type, blob)) - { + if (this.onExportTemplateCallback(action.type, blob)) { return; } } @@ -124,8 +127,7 @@ export default function (REG: ActionHandlerRegistry) { const blob = new Blob([htmlString]); if (this.onExportTemplateCallback != null) { - if (this.onExportTemplateCallback(action.type, blob)) - { + if (this.onExportTemplateCallback(action.type, blob)) { return; } } @@ -148,8 +150,7 @@ export default function (REG: ActionHandlerRegistry) { }); if (this.onExportTemplateCallback != null) { this.emit(AppStore.EVENT_EXPORT_TEMPLATE, base64); - if (this.onExportTemplateCallback("json", blob)) - { + if (this.onExportTemplateCallback("json", blob)) { return; } } diff --git a/src/app/stores/action_handlers/exports.ts b/src/app/stores/action_handlers/exports.ts deleted file mode 100644 index 21ea6f67b..000000000 --- a/src/app/stores/action_handlers/exports.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -import { Actions } from "../../actions"; -import { AppStore } from "../app_store"; -import { ActionHandlerRegistry } from "./registry"; - -export default function (REG: ActionHandlerRegistry) { - REG.add(Actions.SaveExportTemplatePropertyName, function (action) { - this.setPropertyExportName(action.propertyName, action.value); - }); -} diff --git a/src/app/stores/app_store.ts b/src/app/stores/app_store.ts index 018544924..20a60f210 100644 --- a/src/app/stores/app_store.ts +++ b/src/app/stores/app_store.ts @@ -33,8 +33,7 @@ import { stringToDataURL, } from "../utils"; import { - renderChartToLocalString, - renderChartToString, + renderLocalSVG, } from "../views/canvas/chart_display"; import { ActionHandlerRegistry, @@ -343,22 +342,6 @@ export class AppStore extends BaseStore { } } - public renderSVG() { - const svg = - '' + - renderChartToString(this.dataset, this.chart, this.chartState); - return svg; - } - - public async renderLocalSVG() { - const svg = await renderChartToLocalString( - this.dataset, - this.chart, - this.chartState - ); - return '' + svg; - } - public handleAction(action: Actions.Action) { this.actionHandlers.handleAction(this, action); } @@ -434,7 +417,7 @@ export class AppStore extends BaseStore { public deleteChartScale(scaleID: string) { const chart = this.chart; chart.scales - .filter(s => s._id == scaleID) + .filter(s => s._id === scaleID) .forEach((scale) => { this.chartManager.removeScale(scale) }); @@ -455,7 +438,7 @@ export class AppStore extends BaseStore { const chart = await this.backend.get(this.currentChartID); this.updateChartState(); chart.data.state = this.saveState(); - const svg = stringToDataURL("image/svg+xml", await this.renderLocalSVG()); + const svg = stringToDataURL("image/svg+xml", await renderLocalSVG()); const png = await renderDataURLToPNG(svg, { mode: "thumbnail", thumbnail: [200, 150], @@ -477,7 +460,7 @@ export class AppStore extends BaseStore { public async backendSaveChartAs(name: string) { this.updateChartState(); const state = this.saveState(); - const svg = stringToDataURL("image/svg+xml", await this.renderLocalSVG()); + const svg = stringToDataURL("image/svg+xml", await renderLocalSVG()); const png = await renderDataURLToPNG(svg, { mode: "thumbnail", thumbnail: [200, 150], @@ -557,7 +540,7 @@ export class AppStore extends BaseStore { public getTable(name: string): Dataset.Table { if (this.dataset != null) { - return this.dataset.tables.filter((d) => d.name == name)[0]; + return this.dataset.tables.find((d) => d.name === name) || null; } else { return null; } @@ -567,13 +550,6 @@ export class AppStore extends BaseStore { return this.dataset.tables; } - public getColumnVector( - table: Dataset.Table, - columnName: string - ): Dataset.DataValue[] { - return table.rows.map((d) => d[columnName]); - } - public saveSelectionState(): SelectionState { const selection: SelectionState = {}; if (this.currentSelection instanceof ChartElementSelection) { @@ -785,6 +761,10 @@ export class AppStore extends BaseStore { }); } let table = this.getTable(tableName); + if (!table) { + // table not found, we can't infer a scale + return null; + } // compares the ranges of two expression to determine similarity const compareDomainRanges = ( diff --git a/src/app/views/canvas/chart_display.tsx b/src/app/views/canvas/chart_display.tsx index bd1af889f..2fb90c64b 100644 --- a/src/app/views/canvas/chart_display.tsx +++ b/src/app/views/canvas/chart_display.tsx @@ -68,6 +68,15 @@ export function renderChartToString( return ReactDOMServer.renderToString(); } +export async function renderLocalSVG() { + const svg = await renderChartToLocalString( + this.dataset, + this.chart, + this.chartState + ); + return '' + svg; +} + export function renderChartToLocalString( dataset: Dataset.Dataset, chart: Specification.Chart, diff --git a/src/app/views/file_view/export_view.tsx b/src/app/views/file_view/export_view.tsx index 50acb3e19..25673cd1e 100644 --- a/src/app/views/file_view/export_view.tsx +++ b/src/app/views/file_view/export_view.tsx @@ -23,6 +23,7 @@ import { Button } from "@fluentui/react-button"; import { ArrowExportRegular } from "@fluentui/react-icons"; import { tokens } from "@fluentui/react-components"; +import { SaveExportTemplatePropertyName } from "../../actions/actions"; export class InputGroup extends React.Component< React.PropsWithChildren<{ value: string; @@ -421,7 +422,11 @@ export class ExportTemplateView extends React.Component< displayName || targetProperties[property.name], property.default, (value) => { - this.props.store.setPropertyExportName(property.name, value); + + this.props.store.dispatcher.dispatch( + new SaveExportTemplatePropertyName(property.name, value) + ); + this.setState({ targetProperties: { ...targetProperties, diff --git a/src/app/views/file_view/import_view.tsx b/src/app/views/file_view/import_view.tsx index b664f704d..01f17c3fb 100644 --- a/src/app/views/file_view/import_view.tsx +++ b/src/app/views/file_view/import_view.tsx @@ -113,9 +113,9 @@ export class FileViewImport extends ContextedComponent< {strings.templateImport.title} {tables && - tables.map((table) => { + tables.map((table, index) => { return ( - +

{table.type} table: {table.name}

{this.props.mode === MappingMode.ImportTemplate diff --git a/src/app/views/file_view/index.tsx b/src/app/views/file_view/index.tsx index d47da3960..1ffc815a1 100644 --- a/src/app/views/file_view/index.tsx +++ b/src/app/views/file_view/index.tsx @@ -45,6 +45,7 @@ import { strings } from "../../../strings"; import { MainReactContext } from "../../context_component"; import { DatasetsView } from "./datasets_view"; import { tokens } from "@fluentui/react-components"; +import { renderLocalSVG } from "../canvas/chart_display"; export enum MainTabs { about = "about", @@ -79,7 +80,7 @@ export class CurrentChartView extends React.PureComponent< this.renderImage(); } public async renderImage() { - const svg = await this.props.store.renderLocalSVG(); + const svg = await renderLocalSVG(); this.setState({ svgDataURL: stringToDataURL("image/svg+xml", svg), }); From b37978c363a6d8dcf41d4e187f3ac1728c7313a3 Mon Sep 17 00:00:00 2001 From: Ilfat Galiev Date: Sun, 7 Sep 2025 14:20:31 -0800 Subject: [PATCH 3/3] Fix tick --- src/app/views/panels/widgets/fluentui_value_editor.tsx | 1 + src/core/prototypes/links/index.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/views/panels/widgets/fluentui_value_editor.tsx b/src/app/views/panels/widgets/fluentui_value_editor.tsx index 2089ac2dc..341053489 100644 --- a/src/app/views/panels/widgets/fluentui_value_editor.tsx +++ b/src/app/views/panels/widgets/fluentui_value_editor.tsx @@ -125,6 +125,7 @@ export class FluentValueEditor extends ContextedComponent< } return (