Skip to content

Commit

Permalink
86 legendbox refinement (#87)
Browse files Browse the repository at this point in the history
* When writing Latex in LegendBox, only the expression with inserted values appears

Related to #86

* Add a bit space between LegendBox and the top/left

* Add props to Point which give the ability to:
- show/hide name from Points in the grid (showName)
- give Points custom names (customName)
- display only x or only y coordinates for points in LegendBox (legendCoordinates)

Related to #86

* Make it possible to use States without them being displayed in LegendBox

Related to #86

* States can now be added both before and after Latex-strings in constructor and with addElement met

* Make sure it is not possible to add the same element twice to the LegendBox

* Remove parenthesis from when only showing x or y coordinates on point

* Create LegendText object
- LegendText replaces/extends the possibility of adding LaTex-strings to the LegendBox
- LegendText is initialized with an expression-string and some optionals: color for icon-color,  shape for icon-shape and useStates for deciding if the expression should listen to defined states

This gives more freedom and flexibility in adding text, LaTeX and the use of states in LegendBox

Related to #86

* Lint fix and make spacing between LegendBox and top&left a bit smaller

* Export LegendText in index.ts
  • Loading branch information
amaliejvik authored Aug 5, 2024
1 parent 47c1d2d commit 2e681ae
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 48 deletions.
78 changes: 53 additions & 25 deletions src/Components/LegendBox.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import renderToString from "katex";
import LegendText from "./LegendText";
import Plot from "./Plot";
import Point from "./Point";
import State from "./State";
import { Component, GuiComponent } from "./interfaces";

class LegendBox implements GuiComponent {
elements: (Component | string | State<number>)[];
elements: (Component | LegendText | State<number>)[];
states: { [key: string]: State<number> };
htmlElement: HTMLElement;

constructor(elements?: (Component | string | State<number>)[]) {
this.elements = elements || [];
constructor(elements?: (Component | LegendText | State<number>)[]) {
this.elements = [];
if (elements) {
for (const element of elements || []) {
if (!this.elements.includes(element)) {
this.elements.push(element);
}
}
}
this.states = {};
this.htmlElement = this.createLegendBoxWrapper();
const button = this.createSizeAdjustButton();
Expand Down Expand Up @@ -54,8 +62,13 @@ class LegendBox implements GuiComponent {
this.elements.forEach((element) => {
if (element instanceof State) {
this.states[element.getStateName()] = element;
} else if (typeof element === "string") {
this.addStringAsObserver(element);
for (const legendText of this.elements) {
if (legendText instanceof LegendText && legendText.getUseStates()) {
this.addStringAsObserver(legendText.getExpression());
}
}
} else if (element instanceof LegendText && element.getUseStates()) {
this.addStringAsObserver(element.getExpression());
}
});
}
Expand All @@ -82,9 +95,14 @@ class LegendBox implements GuiComponent {
this.elements.length === 0 ? "none" : "block";
}

private processElement(element: Component | string | State<number>) {
private processElement(element: Component | LegendText | State<number>) {
const functionContainer = document.createElement("div");
functionContainer.className = "function-container";

if (element instanceof State && !element.inLegend) {
return;
}

const icon = this.createIcon(element);
functionContainer.appendChild(icon);

Expand All @@ -103,7 +121,7 @@ class LegendBox implements GuiComponent {
this.htmlElement.appendChild(functionContainer);
}

private createIcon(element: Component | string | State<number>) {
private createIcon(element: Component | LegendText | State<number>) {
const icon = document.createElement("span");
icon.className = this.getIconClass(element);
if (icon.className === "triangle-icon") {
Expand All @@ -114,27 +132,30 @@ class LegendBox implements GuiComponent {
return icon;
}

private getTextToDisplay(element: Component | string | State<number>) {
private getTextToDisplay(element: Component | LegendText | State<number>) {
let textToDisplay = "";

if (typeof element === "string") {
textToDisplay =
element + ": " + this.replaceStateNamesWithValues(element);
} else if (element instanceof State) {
if (element instanceof State) {
textToDisplay =
element.getStateName() + ": " + element.getState().toFixed(1);
} else if (element instanceof LegendText) {
if (element.getUseStates()) {
textToDisplay = this.replaceStateNamesWithValues(
element.getExpression()
);
} else {
textToDisplay = element.getExpression();
}
} else if (element instanceof Component) {
textToDisplay = element.getDisplayText
? element.getName() + ": " + element.getDisplayText()
: element.getName();
}

return textToDisplay;
}

private createHtmlElementText(
renderedEquation: string,
element: Component | string | State<number>
element: Component | LegendText | string | State<number>
) {
const htmlElementText = document.createElement("div");
htmlElementText.innerHTML = renderedEquation;
Expand Down Expand Up @@ -164,27 +185,29 @@ class LegendBox implements GuiComponent {
});
}

private getIconClass(element: Component | string | State<number>) {
if (typeof element === "string" || element instanceof State) {
return "point-icon";
private getIconClass(element: Component | LegendText | State<number>) {
if (element instanceof State || element instanceof Point) {
return "circle-icon";
} else if (element instanceof Plot) {
return "plot-icon";
} else if (element instanceof Point) {
return "point-icon";
return "rectangle-icon";
} else if (element instanceof LegendText) {
return element.getIcon();
} else {
return "triangle-icon";
}
}

private getIconColor(element: Component | string | State<number>) {
private getIconColor(element: Component | LegendText | State<number>) {
if (element instanceof Component) {
return "#" + element.getColorAsString();
} else if (element instanceof LegendText) {
return element.getColor();
} else {
return "#faa307";
}
}

public addElement(element: Component | string | State<number>) {
public addElement(element: Component | LegendText | State<number>) {
if (this.elements.includes(element)) {
return;
}
Expand All @@ -196,11 +219,16 @@ class LegendBox implements GuiComponent {
return;
}
this.states[element.getStateName()] = element;
for (const expressions of this.elements) {
if (expressions instanceof LegendText && expressions.getUseStates()) {
this.addStringAsObserver(expressions.getExpression());
}
}
}

// If the element is a string, add it as an observer to the state variables it contains
if (typeof element === "string") {
this.addStringAsObserver(element);
if (element instanceof LegendText && element.getUseStates()) {
this.addStringAsObserver(element.getExpression());
}

this.updateComponents();
Expand Down
59 changes: 59 additions & 0 deletions src/Components/LegendText.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
type LegendTextOptions = {
color?: string;
shape?: string;
useStates?: boolean;
};

const defaultLegendTextOptions = {
color: "#faa307",
shape: "circle",
useStates: false,
};

class LegendText {
private expression: string;
private color: string;
private shape: string;
private useStates: boolean;

constructor(expression: string, options?: LegendTextOptions) {
const { color, shape, useStates } = {
...defaultLegendTextOptions,
...options,
};
this.expression = expression;
this.color = color;
this.shape = shape;
this.useStates = useStates;
}

getExpression(): string {
return this.expression;
}

getColor(): string {
return this.color;
}

getShape(): string {
return this.shape;
}

getUseStates(): boolean {
return this.useStates;
}

getIcon(): string {
switch (this.getShape()) {
case "circle":
return "circle-icon";
case "rectangle":
return "rectangle-icon";
case "triangle":
return "triangle-icon";
default:
return "circle-icon";
}
}
}
export default LegendText;
76 changes: 56 additions & 20 deletions src/Components/Point.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ type PointOptions = {
color?: string;
draggable?: Draggable;
dragListeners?: ((point: Point) => void)[];
customName?: string;
showName?: boolean;
legendCoordinates?: string;
};

const defaultPointOptions = {
Expand All @@ -26,23 +29,38 @@ const defaultPointOptions = {
decimals: 1,
label: false,
dragListeners: [],
showName: true,
legendCoordinates: "",
};

class Point extends Component implements Collider, DragListener<Point> {
private pointName: string | undefined;
private showName: boolean;
private legendCoordinates: string;
private static pointCounter = 0;
static emitter = new EventEmitter();
private color: string;
dragListeners: ((point: Point) => void)[];
constructor(x = 0, y = 0, options?: PointOptions) {
super();
const { color, draggable, decimals, label, dragListeners } = {
const {
color,
draggable,
decimals,
label,
dragListeners,
customName,
showName,
legendCoordinates,
} = {
...defaultPointOptions,
...options,
};
//set point name and color
this.setPointName();
this.setPointName(customName);
this.color = color;
this.showName = showName;
this.legendCoordinates = legendCoordinates;

// set position of the point instance
this.draggable = draggable;
Expand Down Expand Up @@ -79,16 +97,18 @@ class Point extends Component implements Collider, DragListener<Point> {
}

//add name of point
const nameText = new Text(this.pointName, {
color: "black",
fontSize: 18,
anchorY: "middle",
anchorX: "left",
position: [15, 0],
responsiveScale: false,
});
nameText.name = "name";
this.add(nameText);
if (this.showName) {
const nameText = new Text(this.pointName, {
color: "black",
fontSize: 18,
anchorY: "middle",
anchorX: "left",
position: [15, 0],
responsiveScale: false,
});
nameText.name = "name";
this.add(nameText);
}
}

addDragListener(listener: (point: Point) => void) {
Expand Down Expand Up @@ -141,19 +161,35 @@ class Point extends Component implements Collider, DragListener<Point> {
public setPosition(x: number, y: number) {
this.position.set(x, y, this.position.z);
}
private setPointName() {
this.pointName = String.fromCharCode(
"A".charCodeAt(0) + Point.pointCounter
);
Point.pointCounter++;
private setPointName(customName?: string) {
if (customName) {
this.pointName = customName;
} else {
this.pointName = String.fromCharCode(
"A".charCodeAt(0) + Point.pointCounter
);
Point.pointCounter++;
}
}

public getName(): string {
return this.pointName as string;
}

public getDisplayText(): string {
return (
"(" + this.position.x.toFixed(1) + ", " + this.position.y.toFixed(1) + ")"
);
if (this.legendCoordinates === "x") {
return this.position.x.toFixed(1);
} else if (this.legendCoordinates === "y") {
return this.position.y.toFixed(1);
} else {
return (
"(" +
this.position.x.toFixed(1) +
", " +
this.position.y.toFixed(1) +
")"
);
}
}

public hover() {
Expand Down
16 changes: 15 additions & 1 deletion src/Components/State.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,28 @@ import EventEmitter from "eventemitter3";

const STATE_CHANGE_EVENT = "stateChange";

type StateOptions = {
inLegend?: boolean;
};

const defaultStateOptions = {
inLegend: true,
};

class State<T> {
private stateName: string;
private state: T;
private emitter: EventEmitter;
public inLegend: boolean;

constructor(stateName: string, initialState: T) {
constructor(stateName: string, initialState: T, options?: StateOptions) {
const { inLegend } = {
...defaultStateOptions,
...options,
};
this.stateName = stateName;
this.state = initialState;
this.inLegend = inLegend;
this.emitter = new EventEmitter();
}

Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { default as InfiniteLine } from "./Components/InfiniteLine";
export { default as InputField } from "./Components/InputField";
export { default as Label } from "./Components/Label";
export { default as Latex } from "./Components/Latex";
export { default as LegendText } from "./Components/LegendText";
export { default as LegendBox } from "./Components/LegendBox";
export { default as Line } from "./Components/Line";
export { default as Plot } from "./Components/Plot";
Expand Down
Loading

0 comments on commit 2e681ae

Please sign in to comment.