Skip to content

Commit

Permalink
Merge pull request #104 from Taylor-CCB-Group/add_chart_dialog
Browse files Browse the repository at this point in the history
New version of Add Chart, and work on features for more sophisticated column-selection components and dataset links
  • Loading branch information
xinaesthete authored Nov 1, 2024
2 parents 8dab259 + 1bed0cd commit bbb9185
Show file tree
Hide file tree
Showing 32 changed files with 1,113 additions and 232 deletions.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,18 @@
},
"homepage": "https://github.com/Taylor-CCB-Group/MDV#readme",
"dependencies": {
"@deck.gl-community/editable-layers": "^9.0.3",
"@deck.gl-community/layers": "^9.0.3",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@fontsource/roboto": "^5.0.8",
"@vivjs-experimental/viv": "^1.0.0",
"@luma.gl/core": "~9.0.27",
"@mui/icons-material": "^5.14.15",
"@mui/material": "^5.14.15",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-radio-group": "^1.1.3",
"@vivjs-experimental/viv": "^1.0.0",
"axios": "^1.6.8",
"canvas-to-svg": "^1.0.3",
"class-variance-authority": "^0.7.0",
Expand All @@ -63,8 +65,6 @@
"d3-geo": "^3.0.1",
"d3-sankey": "^0.12.3",
"deck.gl": "~9.0.33",
"@deck.gl-community/editable-layers": "^9.0.3",
"@deck.gl-community/layers": "^9.0.3",
"docdash": "^2.0.0",
"dompurify": "^2.4.1",
"dotenv": "^16.0.3",
Expand Down Expand Up @@ -95,6 +95,7 @@
"use-debounce": "^10.0.2",
"uuid": "^10.0.0",
"wordcloud": "^1.2.2",
"zod": "^3.23.8",
"zustand": "^4.4.5"
},
"devDependencies": {
Expand Down
44 changes: 30 additions & 14 deletions src/charts/BaseChart.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { makeAutoObservable, action } from "mobx";
class BaseChart {
/**
* The base constructor
* @param {DataStore} dataStore - The datastore object that contains the data for this chart
* @param {import("./charts.js").DataStore} dataStore - The datastore object that contains the data for this chart
* @param {string | HTMLDivElement} div - The id of the div element or the element itself to house the chart
* @param {Object} config - The config describing the chart
*/
Expand Down Expand Up @@ -196,6 +196,10 @@ class BaseChart {
width: this.width - 5,
};
}
/** @returns {import("./charts.js").DataSource} */
get dataSource() {
return window.mdv.chartManager.charts[this.config.id].dataSource;
}

getFilter() {}

Expand Down Expand Up @@ -486,7 +490,9 @@ class BaseChart {
}
// dynamic props?
}

/**
* @typedef {Array<import("./charts.js").GuiSpec<import("./charts.js").GuiSpecType>>} Settings
*/
/**
* Returns information about which controls to add to the settings dialog.
* Subclasses should call this method and then add their own controls e.g.
Expand All @@ -506,9 +512,11 @@ class BaseChart {
* }]);
* }
* </pre>
* @returns {Settings} - an array of objects describing tweakable parameters
*/
getSettings() {
const c = this.config;
/** @type {Settings} */
const settings = [
{
type: "text",
Expand All @@ -523,7 +531,7 @@ class BaseChart {

if (colorOptions.colorby) {
//cannot color by unique (at the moment)
const filter =
const filter = (
colorOptions.colorby === "all"
? [
"int32",
Expand All @@ -533,14 +541,22 @@ class BaseChart {
"text16",
"multitext",
]
: colorOptions.colorby;
const cols = this.dataStore.getColumnList(filter);
cols.push({ name: "None", field: "_none" });
: colorOptions.colorby
);

const colorSettings = [];
settings.push({
type: "folder",
label: "Color Settings",
current_value: colorSettings,
});

// change this to use setting with type: "column" - so it should understand "filter" in a similar way
colorSettings.push({
label: "Color By",
type: "dropdown",
values: [cols, "name", "field"],
current_value: c.color_by || "_none",
type: "column",
current_value: c.color_by,
// filter: filter,
func: (x) => {
if (x === "_none") {
c.color_by = undefined;
Expand All @@ -552,7 +568,7 @@ class BaseChart {
},
});
if (colorOptions.color_overlay !== undefined) {
settings.push({
colorSettings.push({
label: "Color Overlay",
type: "slider",
current_value: c.color_overlay,
Expand All @@ -562,7 +578,7 @@ class BaseChart {
},
});
}
settings.push({
colorSettings.push({
label: "Show Color Legend",
type: "check",

Expand All @@ -575,7 +591,7 @@ class BaseChart {
this.setColorLegend();
},
});
settings.push({
colorSettings.push({
label: "SymLog Color Scale",
type: "check",

Expand All @@ -587,7 +603,7 @@ class BaseChart {
}
},
});
settings.push({
colorSettings.push({
label: "Treat zero as missing",
type: "check",

Expand All @@ -599,7 +615,7 @@ class BaseChart {
}
},
});
settings.push({
colorSettings.push({
type: "radiobuttons",
label: "Trim Color Scale to Percentile",
current_value: c.trim_color_scale || "none",
Expand Down
46 changes: 32 additions & 14 deletions src/charts/ChartManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import { addChartLink } from "../links/link_utils";
import { toPng } from "html-to-image";
import popoutChart from "@/utilities/Popout";
import { makeObservable, observable, action } from "mobx";
import { AddChartDialog } from "./dialogs/AddChartDialog";
//import { AddChartDialog } from "./dialogs/AddChartDialog";

//order of column data in an array buffer
//doubles and integers (both represented by float32) and int32 need to be first
Expand Down Expand Up @@ -113,7 +113,7 @@ function listenPreferredColorScheme(callback) {
/**
* The object to manage charts {@tutorial chartmanager}
*
* @param {string|DOMelement} div - The DOM element or id of the element to house the app
* @param {string|HTMLElement} div - The DOM element or id of the element to house the app
* @param {object[]} datasources - An array of datasource configs -see {@tutorial datasource}.
* Each config must contain the size parameter, giving the number of rows in the DataStore.
* @param {object} dataloader - An object containing the following
Expand All @@ -134,7 +134,8 @@ function listenPreferredColorScheme(callback) {
* @param {string} [config.permisson] the level of permission the user has. This just makes certain
* options unavaliable. Any logic should be handled when a state_saved event is broadcast
* @param {boolean} [config.gridstack] whether to arrange the charts in a grid
* @param
* @param {object} [listener] - An object with functions to listen to events.
* beware: the way 'event listeners' are implemented is highly unorthodox and may be confusing.
*
*/
class ChartManager {
Expand All @@ -149,6 +150,7 @@ class ChartManager {
}
// manage global singleton
if (!window.mdv) {
// @ts-ignore this is unclean but somewhat ok
window.mdv = {};
}
if (window.mdv.chartManager) {
Expand Down Expand Up @@ -550,8 +552,8 @@ class ChartManager {
}

/**
* @param {ds} DataStore
* @param {ods} DataStore other data store
* @param {DataStore} ds
* @param {DataStore} ods other data store
* @param {object} link - information about what properties to target in ods
* For instance, `{ source_column: <column_name>, target_chart: <chart_id>, target_property: <property_name> }`
* more concretely: `{ source_column: "feature", target_chart: "quadrat_image_chart", target_property: "color_by" }`
Expand Down Expand Up @@ -626,6 +628,7 @@ class ChartManager {
//would be nice if this could be maybe async / lazy / different ways of composing filters?

const args = [info[c1], info[c2]];
// @ts-ignore should revisit this at some point
args.operand = "and"; //force multitext to use "and"

interactionFilter.filter(
Expand Down Expand Up @@ -1333,7 +1336,7 @@ class ChartManager {
const initialCharts = {};
const updatedColumns = {};
const metadata = {};
const twidth = this.contentDiv.offsetWidth;
// const twidth = this.contentDiv.offsetWidth;
for (const ds of this.dataSources) {
if (ds.contentDiv) {
initialCharts[ds.name] = [];
Expand Down Expand Up @@ -1379,6 +1382,7 @@ class ChartManager {
const view = JSON.parse(JSON.stringify(this.viewData));
view.initialCharts = initialCharts;
const all_views = this.viewSelect
// @ts-ignore do we know that we actually have elements with 'value'?
? Array.from(this.viewSelect.children, (x) => x.value)
: null;

Expand Down Expand Up @@ -1516,10 +1520,14 @@ class ChartManager {
* @param {string} dataSource The name of the dataSource
* @param {function} callback A function which will be run once all the
* columns are loaded
* @param {integer} [split=10] the number of columns to send with each request
* @param {integer} [threads=2] the number of concurrent requests
* @param {number} [split=10] the number of columns to send with each request
* @param {number} [threads=2] the number of concurrent requests
*/
loadColumnSet(columns, dataSource, callback, split = 10, threads = 2) {
if (columns.length === 0) {
callback(); //should there be any args? hard to tell without types
return;
}
const id = getRandomString();
const lc = this.config.dataloading || {};
split = lc.split || 10;
Expand Down Expand Up @@ -1625,6 +1633,7 @@ class ChartManager {
position: "bottom-right",
},
func: () => {
// new BaseDialog.experiment["AddColumnsFromRows"](ds, ds_to, link, this);
new AddColumnsFromRowsDialog(ds, ds_to, link, this);
},
},
Expand All @@ -1642,9 +1651,10 @@ class ChartManager {
position: "bottom-right",
},
func: () => {
new AddChartDialog(ds, (config) =>
this.addChart(ds.name, config, true),
);
// new AddChartDialog(ds, (config) =>
// this.addChart(ds.name, config, true),
// );
new BaseDialog.experiment["AddChartDialogReact"](dataStore);
},
},
ds.menuBar,
Expand Down Expand Up @@ -1866,7 +1876,7 @@ class ChartManager {
left: `${left}px`,
top: `${top}px`,
background: t.main_panel_color,
zIndex: 2,
zIndex: "2",
display: "flex",
alignItems: "center",
justifyContent: "center",
Expand Down Expand Up @@ -1929,7 +1939,7 @@ class ChartManager {
backdropFilter: "blur(10px)",
},
},
div.lastChild,
div.lastElementChild,
);
debugNode.innerHTML = `<div><h2>Error creating chart</h2><pre>${error.stack}</pre></div>`;
debugNode.onclick = () => debugNode.remove();
Expand Down Expand Up @@ -2141,6 +2151,7 @@ class ChartManager {
dataSource: ds,
};
this._makeChartRD(chart, ds);
// @ts-ignore
chart.popoutIcon = chart.addMenuIcon(
"fas fa-external-link-alt",
"popout",
Expand All @@ -2162,19 +2173,23 @@ class ChartManager {

//need to decorate any method that uses column data as data may
//have to be loaded before method can execute
// @ts-ignore
if (chart.colorByColumn) {
this._decorateColumnMethod("colorByColumn", chart, dataSource);
}
// @ts-ignore
if (chart.setToolTipColumn) {
this._decorateColumnMethod("setToolTipColumn", chart, dataSource);
}
// @ts-ignore
if (chart.setBackgroundFilter) {
this._decorateColumnMethod(
"setBackgroundFilter",
chart,
dataSource,
);
}
// @ts-ignore
if (chart.changeContourParameter) {
this._decorateColumnMethod(
"changeContourParameter",
Expand All @@ -2190,6 +2205,7 @@ class ChartManager {
}
}

// @ts-ignore
if (chart.setupLinks) {
//phasing out
if (ds.index_link_to) {
Expand All @@ -2210,11 +2226,13 @@ class ChartManager {

//I think this is obsolete now
const cll = ds.column_link_to;
// @ts-ignore
if (cll && chart.createColumnLinks) {
const func = (columns, callback) => {
//make sure index is loaded before use
this._getColumnsThen(cll.dataSource, columns, callback);
};
// @ts-ignore
chart.createColumnLinks(
this.dsIndex[cll.dataSource].dataStore,
cll.columns,
Expand Down Expand Up @@ -2407,7 +2425,7 @@ class ChartManager {
bottom: "40px",
right: "40px",
fontSize: "18px",
zIndex: 100,
zIndex: "100",
},
},
this.contentDiv,
Expand Down
5 changes: 3 additions & 2 deletions src/charts/ChartTypes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type DataStore from "../datastore/DataStore";
import type BaseChart from "./BaseChart";
import type { DataSource, ExtraControl, GuiSpecType } from "./charts";
import type { ExtraControl, GuiSpecType } from "./charts";

//new Set(Object.values(BaseChart.types).flatMap(t => t.params).filter(Boolean).flatMap(p => p.type))
/** annotation of what kind of column type a given param will accept */
Expand All @@ -19,12 +19,13 @@ export type ChartType<T extends BaseChart> = {
/** The human-readable name that will appear in the 'add chart' dialog etc. */
name: string;

required?: string[] | ((ds: DataSource) => unknown); //TODO: better typing here (& there & everywhere)
required?: string[] | ((ds: DataStore) => boolean);
init?: (config: any, dataSource: any, extraControls: any) => void;
extra_controls?: (dataStore: DataStore) => ExtraControl<GuiSpecType>[]; //not GuiSpec<GuiSpecType>[]; - AddChartDialog & SettingsDialog behave differently - would like to unify
params?: { type: Param | Param[]; name: string }[];
configEntriesUsingColumns?: string[];
methodsUsingColumns?: string[]; // (keyof T)[]; //would like better typing here
allow_user_add?: boolean;
};

export type ChartTypeMap = Record<string, ChartType<BaseChart>>;
Expand Down
Loading

0 comments on commit bbb9185

Please sign in to comment.