@teachinglab/omd 0.7.20 → 0.7.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/canvas/events/eventManager.js +8 -3
- package/canvas/ui/cursor.js +2 -2
- package/package.json +1 -1
- package/src/index.js +1 -0
- package/src/omdExpression.js +61 -0
- package/src/omdFactory.js +3 -1
- package/src/omdLabel.js +66 -0
|
@@ -214,10 +214,15 @@ export class EventManager {
|
|
|
214
214
|
// Show custom cursor when entering canvas
|
|
215
215
|
if (this.canvas.cursor) {
|
|
216
216
|
this.canvas.cursor.show();
|
|
217
|
-
//
|
|
217
|
+
// Ensure the cursor shape matches the active tool (fixes stale crosshair on first entry)
|
|
218
218
|
const activeTool = this.canvas.toolManager.getActiveTool();
|
|
219
|
-
if (activeTool
|
|
220
|
-
|
|
219
|
+
if (activeTool) {
|
|
220
|
+
if (activeTool.getCursor) {
|
|
221
|
+
this.canvas.cursor.setShape(activeTool.getCursor());
|
|
222
|
+
}
|
|
223
|
+
if (activeTool.config) {
|
|
224
|
+
this.canvas.cursor.updateFromToolConfig(activeTool.config);
|
|
225
|
+
}
|
|
221
226
|
}
|
|
222
227
|
}
|
|
223
228
|
// If mouse is down (event.buttons !== 0), start drawing
|
package/canvas/ui/cursor.js
CHANGED
|
@@ -60,8 +60,8 @@ export class Cursor {
|
|
|
60
60
|
this.element.appendChild(shape);
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
// Show
|
|
64
|
-
this.setShape('
|
|
63
|
+
// Show pencil (blue dot) shape initially so the first visible cursor is always a dot
|
|
64
|
+
this.setShape('pencil');
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/**
|
package/package.json
CHANGED
package/src/index.js
CHANGED
package/src/omdExpression.js
CHANGED
|
@@ -8,6 +8,7 @@ import { omdNumber } from "./omdNumber.js";
|
|
|
8
8
|
import { omdVariable } from "./omdVariable.js";
|
|
9
9
|
import { omdMetaExpression } from "./omdMetaExpression.js"
|
|
10
10
|
import { parseExpressionString } from "./omdUtils.js";
|
|
11
|
+
import { getNodeForAST } from "../omd/core/omdUtilities.js";
|
|
11
12
|
|
|
12
13
|
export class omdExpression extends omdMetaExpression
|
|
13
14
|
{
|
|
@@ -21,6 +22,7 @@ export class omdExpression extends omdMetaExpression
|
|
|
21
22
|
this.operatorSet = [];
|
|
22
23
|
|
|
23
24
|
this.inset = 5;
|
|
25
|
+
this.expressionNode = null;
|
|
24
26
|
|
|
25
27
|
this.expressionStack = new jsvgLayoutGroup();
|
|
26
28
|
this.expressionStack.setPosition( this.inset, 0 );
|
|
@@ -28,15 +30,56 @@ export class omdExpression extends omdMetaExpression
|
|
|
28
30
|
this.addChild( this.expressionStack );
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
_renderWithExpressionNode(expressionString, fontSize) {
|
|
34
|
+
if (typeof math === 'undefined' || typeof math.parse !== 'function') {
|
|
35
|
+
throw new Error('math.js is required to parse expression strings');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const ast = math.parse(expressionString);
|
|
39
|
+
const NodeType = getNodeForAST(ast);
|
|
40
|
+
const exprNode = new NodeType(ast);
|
|
41
|
+
|
|
42
|
+
if (typeof fontSize === 'number' && exprNode.setFontSize) {
|
|
43
|
+
exprNode.setFontSize(fontSize);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (exprNode.hideBackgroundByDefault) exprNode.hideBackgroundByDefault();
|
|
47
|
+
if (exprNode.computeDimensions) exprNode.computeDimensions();
|
|
48
|
+
if (exprNode.updateLayout) exprNode.updateLayout();
|
|
49
|
+
|
|
50
|
+
if (typeof this.expressionStack.removeAllChildren === 'function') {
|
|
51
|
+
this.expressionStack.removeAllChildren();
|
|
52
|
+
} else {
|
|
53
|
+
this.expressionStack.childList = [];
|
|
54
|
+
}
|
|
55
|
+
this.expressionStack.addChild(exprNode);
|
|
56
|
+
this.expressionStack.setSpacer(0);
|
|
57
|
+
|
|
58
|
+
this.expressionNode = exprNode;
|
|
59
|
+
this.updateLayout();
|
|
60
|
+
}
|
|
61
|
+
|
|
31
62
|
loadFromJSON( data )
|
|
32
63
|
{
|
|
33
64
|
// Accept either structured object or a plain expression string
|
|
34
65
|
if ( typeof data === 'string' ) {
|
|
66
|
+
try {
|
|
67
|
+
this._renderWithExpressionNode(data, data.fontSize);
|
|
68
|
+
return;
|
|
69
|
+
} catch (e) {
|
|
70
|
+
console.warn('⚠️ omdExpression math.js render failed, falling back to legacy parsing:', e?.message || e);
|
|
71
|
+
}
|
|
35
72
|
const parsed = parseExpressionString(data);
|
|
36
73
|
if ( parsed ) data = parsed;
|
|
37
74
|
}
|
|
38
75
|
// Handle object with 'expression' string property
|
|
39
76
|
else if ( typeof data === 'object' && data.expression && typeof data.expression === 'string' ) {
|
|
77
|
+
try {
|
|
78
|
+
this._renderWithExpressionNode(data.expression, data.fontSize);
|
|
79
|
+
return;
|
|
80
|
+
} catch (e) {
|
|
81
|
+
console.warn('⚠️ omdExpression math.js render failed, falling back to legacy parsing:', e?.message || e);
|
|
82
|
+
}
|
|
40
83
|
const parsed = parseExpressionString(data.expression);
|
|
41
84
|
if ( parsed ) {
|
|
42
85
|
// Merge parsed data into data object
|
|
@@ -117,6 +160,24 @@ export class omdExpression extends omdMetaExpression
|
|
|
117
160
|
|
|
118
161
|
updateLayout()
|
|
119
162
|
{
|
|
163
|
+
if (this.expressionNode) {
|
|
164
|
+
const node = this.expressionNode;
|
|
165
|
+
if (node.computeDimensions) node.computeDimensions();
|
|
166
|
+
if (node.updateLayout) node.updateLayout();
|
|
167
|
+
|
|
168
|
+
this.expressionStack.doHorizontalLayout();
|
|
169
|
+
this.expressionStack.setPosition(this.inset, this.inset);
|
|
170
|
+
|
|
171
|
+
const W = node.width || this.expressionStack.width;
|
|
172
|
+
const H = node.height || this.expressionStack.height;
|
|
173
|
+
|
|
174
|
+
this.backRect.setWidthAndHeight( W + this.inset*2, H + this.inset*2 );
|
|
175
|
+
this.setWidthAndHeight( this.backRect.width, this.backRect.height );
|
|
176
|
+
this.width = this.backRect.width;
|
|
177
|
+
this.height = this.backRect.height;
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
120
181
|
this.expressionStack.doHorizontalLayout();
|
|
121
182
|
|
|
122
183
|
var W = this.expressionStack.width;
|
package/src/omdFactory.js
CHANGED
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
omdCircle,
|
|
43
43
|
omdRegularPolygon
|
|
44
44
|
} from './omdShapes.js';
|
|
45
|
+
import { omdLabel } from './omdLabel.js';
|
|
45
46
|
|
|
46
47
|
/**
|
|
47
48
|
* Map of omdType strings to their corresponding class constructors
|
|
@@ -71,7 +72,8 @@ const OMD_TYPE_MAP = {
|
|
|
71
72
|
'rectangle': omdRectangle,
|
|
72
73
|
'ellipse': omdEllipse,
|
|
73
74
|
'circle': omdCircle,
|
|
74
|
-
'regularPolygon': omdRegularPolygon
|
|
75
|
+
'regularPolygon': omdRegularPolygon,
|
|
76
|
+
'label': omdLabel,
|
|
75
77
|
};
|
|
76
78
|
|
|
77
79
|
/**
|
package/src/omdLabel.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { omdMetaExpression } from "./omdMetaExpression.js";
|
|
2
|
+
import { jsvgTextBox } from "@teachinglab/jsvg";
|
|
3
|
+
|
|
4
|
+
const SIZE_MAP = {
|
|
5
|
+
small: 14,
|
|
6
|
+
medium: 20,
|
|
7
|
+
large: 28,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export class omdLabel extends omdMetaExpression
|
|
11
|
+
{
|
|
12
|
+
constructor()
|
|
13
|
+
{
|
|
14
|
+
super();
|
|
15
|
+
|
|
16
|
+
this.type = "omdLabel";
|
|
17
|
+
this.text = "";
|
|
18
|
+
this.size = "medium"; // small | medium | large
|
|
19
|
+
|
|
20
|
+
this.textBox = new jsvgTextBox();
|
|
21
|
+
this.textBox.setFontFamily("Albert Sans");
|
|
22
|
+
this.textBox.setFontColor("black");
|
|
23
|
+
this.textBox.setVerticalCentering();
|
|
24
|
+
this.textBox.setAlignment("left");
|
|
25
|
+
this.addChild(this.textBox);
|
|
26
|
+
|
|
27
|
+
this.backRect.setFillColor("transparent");
|
|
28
|
+
this.backRect.setOpacity(0);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
loadFromJSON(data)
|
|
32
|
+
{
|
|
33
|
+
if (typeof data === 'string') {
|
|
34
|
+
this.text = data;
|
|
35
|
+
} else {
|
|
36
|
+
if (typeof data.text !== "undefined") this.text = data.text;
|
|
37
|
+
if (typeof data.size !== "undefined") this.size = data.size;
|
|
38
|
+
if (typeof data.color !== "undefined") this.textBox.setFontColor(data.color);
|
|
39
|
+
}
|
|
40
|
+
this.updateLayout();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
updateLayout()
|
|
44
|
+
{
|
|
45
|
+
const fontSize = SIZE_MAP[this.size] ?? SIZE_MAP.medium;
|
|
46
|
+
this.textBox.setFontSize(fontSize);
|
|
47
|
+
|
|
48
|
+
const padding = 8;
|
|
49
|
+
const approxCharW = fontSize * 0.58;
|
|
50
|
+
const W = Math.max(60, Math.ceil(String(this.text).length * approxCharW) + padding * 2);
|
|
51
|
+
const H = fontSize + padding * 2;
|
|
52
|
+
|
|
53
|
+
this.textBox.setWidthAndHeight(W, H);
|
|
54
|
+
this.textBox.setText(this.text ?? "");
|
|
55
|
+
|
|
56
|
+
this.backRect.setWidthAndHeight(W, H);
|
|
57
|
+
this.setWidthAndHeight(W, H);
|
|
58
|
+
this.width = W;
|
|
59
|
+
this.height = H;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
hideBackgroundByDefault()
|
|
63
|
+
{
|
|
64
|
+
this.backRect.setOpacity(0);
|
|
65
|
+
}
|
|
66
|
+
}
|