@teachinglab/omd 0.2.7 → 0.2.9
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/drawing/stroke.js +1 -1
- package/canvas/ui/toolbar.js +0 -15
- package/omd/core/omdEquationStack.js +11 -2
- package/omd/display/omdDisplay.js +149 -31
- package/omd/nodes/omdEquationNode.js +46 -6
- package/omd/step-visualizer/omdStepVisualizer.js +311 -29
- package/omd/step-visualizer/omdStepVisualizerLayout.js +122 -110
- package/omd/step-visualizer/omdStepVisualizerTextBoxes.js +46 -8
- package/omd/utils/omdNodeOverlay.js +1 -1
- package/omd/utils/omdPopup.js +1 -1
- package/omd/utils/omdStepVisualizerInteractiveSteps.js +318 -121
- package/omd/utils/omdTranscriptionService.js +10 -6
- package/package.json +7 -4
- package/src/omdBalanceHanger.js +31 -1
- package/src/omdColor.js +1 -0
- package/src/omdCoordinatePlane.js +53 -3
- package/src/omdMetaExpression.js +8 -4
- package/src/omdTable.js +182 -52
|
@@ -9,7 +9,8 @@ export class omdTranscriptionService {
|
|
|
9
9
|
constructor(options = {}) {
|
|
10
10
|
this.options = {
|
|
11
11
|
endpoint: options.endpoint || this._getDefaultEndpoint(),
|
|
12
|
-
defaultProvider: options.defaultProvider || '
|
|
12
|
+
defaultProvider: options.defaultProvider || 'openai',
|
|
13
|
+
defaultModel: options.defaultModel || 'gpt-4o-mini',
|
|
13
14
|
...options
|
|
14
15
|
};
|
|
15
16
|
}
|
|
@@ -60,7 +61,9 @@ export class omdTranscriptionService {
|
|
|
60
61
|
},
|
|
61
62
|
body: JSON.stringify({
|
|
62
63
|
imageBase64: base64Image,
|
|
63
|
-
prompt: options.prompt || 'Please transcribe the handwritten mathematical expression in this image. Return only the mathematical expression in pure format (no LaTeX, no dollar signs). For powers use ^, for fractions use /, for square roots use sqrt().'
|
|
64
|
+
prompt: options.prompt || 'Please transcribe the handwritten mathematical expression in this image. Return only the mathematical expression in pure format (no LaTeX, no dollar signs). For powers use ^, for fractions use /, for square roots use sqrt().',
|
|
65
|
+
model: options.model || this.options.defaultModel,
|
|
66
|
+
provider: options.provider || this.options.defaultProvider
|
|
64
67
|
})
|
|
65
68
|
});
|
|
66
69
|
|
|
@@ -72,8 +75,9 @@ export class omdTranscriptionService {
|
|
|
72
75
|
|
|
73
76
|
|
|
74
77
|
return {
|
|
75
|
-
text: result.text,
|
|
76
|
-
provider: result.provider ||
|
|
78
|
+
text: (result.text || '').trim(),
|
|
79
|
+
provider: result.provider || this.options.defaultProvider,
|
|
80
|
+
model: result.model || (options.model || this.options.defaultModel),
|
|
77
81
|
confidence: result.confidence || 1.0
|
|
78
82
|
};
|
|
79
83
|
|
|
@@ -106,7 +110,7 @@ export class omdTranscriptionService {
|
|
|
106
110
|
* @returns {Array} List of available providers
|
|
107
111
|
*/
|
|
108
112
|
getAvailableProviders() {
|
|
109
|
-
return ['
|
|
113
|
+
return ['openai']; // Server handles actual model execution
|
|
110
114
|
}
|
|
111
115
|
|
|
112
116
|
/**
|
|
@@ -115,6 +119,6 @@ export class omdTranscriptionService {
|
|
|
115
119
|
* @returns {boolean} True if provider is available
|
|
116
120
|
*/
|
|
117
121
|
isProviderAvailable(provider) {
|
|
118
|
-
return provider === '
|
|
122
|
+
return provider === 'openai';
|
|
119
123
|
}
|
|
120
124
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teachinglab/omd",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "omd",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -36,11 +36,14 @@
|
|
|
36
36
|
"express": "^4.18.2",
|
|
37
37
|
"mathjs": "^14.5.2",
|
|
38
38
|
"openai": "^4.28.0",
|
|
39
|
-
"@teachinglab/jsvg": "0.1.1"
|
|
39
|
+
"@teachinglab/jsvg": "0.1.1",
|
|
40
|
+
"@netlify/vite-plugin": "^2.5.4",
|
|
41
|
+
"vite": "^5.4.0"
|
|
40
42
|
},
|
|
41
43
|
"scripts": {
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
+
"dev": "vite",
|
|
45
|
+
"build": "vite build",
|
|
46
|
+
"dev:netlify": "netlify dev"
|
|
44
47
|
},
|
|
45
48
|
"publishConfig": {
|
|
46
49
|
"access": "public"
|
package/src/omdBalanceHanger.js
CHANGED
|
@@ -17,6 +17,12 @@ export class omdBalanceHanger extends jsvgGroup
|
|
|
17
17
|
this.fontFamily = "Albert Sans";
|
|
18
18
|
this.fontSize = 18;
|
|
19
19
|
|
|
20
|
+
// Background customization properties
|
|
21
|
+
this.backgroundColor = omdColor.lightGray;
|
|
22
|
+
this.backgroundCornerRadius = 5;
|
|
23
|
+
this.backgroundOpacity = 1.0;
|
|
24
|
+
this.showBackground = true;
|
|
25
|
+
|
|
20
26
|
this.updateLayout();
|
|
21
27
|
}
|
|
22
28
|
|
|
@@ -37,6 +43,16 @@ export class omdBalanceHanger extends jsvgGroup
|
|
|
37
43
|
if ( typeof data.fontSize != "undefined" )
|
|
38
44
|
this.fontSize = data.fontSize;
|
|
39
45
|
|
|
46
|
+
// Load background customization properties
|
|
47
|
+
if ( typeof data.backgroundColor != "undefined" )
|
|
48
|
+
this.backgroundColor = data.backgroundColor;
|
|
49
|
+
if ( typeof data.backgroundCornerRadius != "undefined" )
|
|
50
|
+
this.backgroundCornerRadius = data.backgroundCornerRadius;
|
|
51
|
+
if ( typeof data.backgroundOpacity != "undefined" )
|
|
52
|
+
this.backgroundOpacity = data.backgroundOpacity;
|
|
53
|
+
if ( typeof data.showBackground != "undefined" )
|
|
54
|
+
this.showBackground = data.showBackground;
|
|
55
|
+
|
|
40
56
|
this.updateLayout();
|
|
41
57
|
}
|
|
42
58
|
|
|
@@ -139,7 +155,21 @@ export class omdBalanceHanger extends jsvgGroup
|
|
|
139
155
|
box.setCornerRadius(5);
|
|
140
156
|
box.setPosition( xOffset-W/2, yOffset + i*40 );
|
|
141
157
|
}
|
|
142
|
-
|
|
158
|
+
|
|
159
|
+
// Apply customizable background properties
|
|
160
|
+
if (this.showBackground) {
|
|
161
|
+
box.setFillColor(this.backgroundColor);
|
|
162
|
+
if (this.backgroundOpacity < 1.0) {
|
|
163
|
+
box.setOpacity(this.backgroundOpacity);
|
|
164
|
+
}
|
|
165
|
+
} else {
|
|
166
|
+
box.setFillColor("transparent");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Only apply corner radius to rectangular boxes (jsvgRect), not ellipses
|
|
170
|
+
if (this.backgroundCornerRadius > 0 && box.setCornerRadius) {
|
|
171
|
+
box.setCornerRadius(this.backgroundCornerRadius);
|
|
172
|
+
}
|
|
143
173
|
|
|
144
174
|
this.addChild( box );
|
|
145
175
|
|
package/src/omdColor.js
CHANGED
|
@@ -33,6 +33,12 @@ export class omdCoordinatePlane extends jsvgGroup {
|
|
|
33
33
|
this.tickLabelOffsetPx = 5; //offset from axes
|
|
34
34
|
this.showTickLabels = true; // show numeric tick labels
|
|
35
35
|
|
|
36
|
+
// Background customization properties
|
|
37
|
+
this.backgroundColor = omdColor.lightGray;
|
|
38
|
+
this.backgroundCornerRadius = 15;
|
|
39
|
+
this.backgroundOpacity = 1.0;
|
|
40
|
+
this.showBackground = true;
|
|
41
|
+
|
|
36
42
|
this.calculatePadding();
|
|
37
43
|
this.updateLayout();
|
|
38
44
|
}
|
|
@@ -62,6 +68,12 @@ export class omdCoordinatePlane extends jsvgGroup {
|
|
|
62
68
|
this.forceAllTickLabels = (data.forceAllTickLabels !== undefined) ? data.forceAllTickLabels : this.forceAllTickLabels;
|
|
63
69
|
this.showTickLabels = (data.showTickLabels !== undefined) ? data.showTickLabels : this.showTickLabels;
|
|
64
70
|
|
|
71
|
+
// Load background customization properties
|
|
72
|
+
this.backgroundColor = data.backgroundColor || this.backgroundColor;
|
|
73
|
+
this.backgroundCornerRadius = (data.backgroundCornerRadius !== undefined) ? data.backgroundCornerRadius : this.backgroundCornerRadius;
|
|
74
|
+
this.backgroundOpacity = (data.backgroundOpacity !== undefined) ? data.backgroundOpacity : this.backgroundOpacity;
|
|
75
|
+
this.showBackground = (data.showBackground !== undefined) ? data.showBackground : this.showBackground;
|
|
76
|
+
|
|
65
77
|
this.calculatePadding();
|
|
66
78
|
this.updateLayout();
|
|
67
79
|
}
|
|
@@ -80,11 +92,20 @@ export class omdCoordinatePlane extends jsvgGroup {
|
|
|
80
92
|
|
|
81
93
|
const whiteRect = new jsvgRect();
|
|
82
94
|
whiteRect.setWidthAndHeight(outerWidth, outerHeight);
|
|
83
|
-
|
|
95
|
+
|
|
96
|
+
// Apply customizable background properties
|
|
97
|
+
if (this.showBackground) {
|
|
98
|
+
whiteRect.setFillColor(this.backgroundColor);
|
|
99
|
+
if (this.backgroundOpacity < 1.0) {
|
|
100
|
+
whiteRect.setOpacity(this.backgroundOpacity);
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
whiteRect.setFillColor("transparent");
|
|
104
|
+
}
|
|
84
105
|
whiteRect.setStrokeWidth(0);
|
|
85
106
|
|
|
86
|
-
if (whiteRect.setCornerRadius) {
|
|
87
|
-
whiteRect.setCornerRadius(
|
|
107
|
+
if (whiteRect.setCornerRadius && this.backgroundCornerRadius > 0) {
|
|
108
|
+
whiteRect.setCornerRadius(this.backgroundCornerRadius);
|
|
88
109
|
}
|
|
89
110
|
|
|
90
111
|
clipMask.addChild(whiteRect);
|
|
@@ -489,4 +510,33 @@ export class omdCoordinatePlane extends jsvgGroup {
|
|
|
489
510
|
holder.addChild(shape);
|
|
490
511
|
}
|
|
491
512
|
}
|
|
513
|
+
|
|
514
|
+
// Background customization methods
|
|
515
|
+
setBackgroundColor(color) {
|
|
516
|
+
this.backgroundColor = color;
|
|
517
|
+
this.updateLayout();
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
setBackgroundCornerRadius(radius) {
|
|
521
|
+
this.backgroundCornerRadius = radius;
|
|
522
|
+
this.updateLayout();
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
setBackgroundOpacity(opacity) {
|
|
526
|
+
this.backgroundOpacity = Math.max(0, Math.min(1, opacity));
|
|
527
|
+
this.updateLayout();
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
setShowBackground(show) {
|
|
531
|
+
this.showBackground = show;
|
|
532
|
+
this.updateLayout();
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
setBackgroundStyle(options = {}) {
|
|
536
|
+
if (options.backgroundColor !== undefined) this.backgroundColor = options.backgroundColor;
|
|
537
|
+
if (options.cornerRadius !== undefined) this.backgroundCornerRadius = options.cornerRadius;
|
|
538
|
+
if (options.opacity !== undefined) this.backgroundOpacity = Math.max(0, Math.min(1, options.opacity));
|
|
539
|
+
if (options.show !== undefined) this.showBackground = options.show;
|
|
540
|
+
this.updateLayout();
|
|
541
|
+
}
|
|
492
542
|
}
|
package/src/omdMetaExpression.js
CHANGED
|
@@ -16,7 +16,7 @@ export class omdMetaExpression extends jsvgGroup
|
|
|
16
16
|
this.backRect = new jsvgRect();
|
|
17
17
|
this.backRect.setWidthAndHeight( 30,30 );
|
|
18
18
|
this.backRect.setCornerRadius( 5 );
|
|
19
|
-
this.backRect.setFillColor(
|
|
19
|
+
this.backRect.setFillColor( this.getBackgroundColor() );
|
|
20
20
|
this.addChild( this.backRect );
|
|
21
21
|
|
|
22
22
|
// Old events for selection - we will replace these with provenance highlighting
|
|
@@ -63,6 +63,10 @@ export class omdMetaExpression extends jsvgGroup
|
|
|
63
63
|
return this.fontSize || 16;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
getBackgroundColor() {
|
|
67
|
+
return this._backgroundStyle?.backgroundColor ?? omdColor.lightGray;
|
|
68
|
+
}
|
|
69
|
+
|
|
66
70
|
// ===== PROVENANCE HIGHLIGHTING =====
|
|
67
71
|
|
|
68
72
|
highlightProvenance(event, color = omdColor.hiliteColor, minStepNumber = -Infinity) {
|
|
@@ -160,7 +164,7 @@ export class omdMetaExpression extends jsvgGroup
|
|
|
160
164
|
this.backRect.setOpacity(1.0);
|
|
161
165
|
} else {
|
|
162
166
|
// Reset to default state
|
|
163
|
-
this.backRect.setFillColor(
|
|
167
|
+
this.backRect.setFillColor(this.getBackgroundColor());
|
|
164
168
|
if (!this.defaultOpaqueBack) {
|
|
165
169
|
this.backRect.setOpacity(0.01);
|
|
166
170
|
}
|
|
@@ -224,7 +228,7 @@ export class omdMetaExpression extends jsvgGroup
|
|
|
224
228
|
|
|
225
229
|
if (root === this && this.parent instanceof omdMetaExpression) return;
|
|
226
230
|
|
|
227
|
-
this.backRect.setFillColor(
|
|
231
|
+
this.backRect.setFillColor( this.getBackgroundColor() );
|
|
228
232
|
if ( this.defaultOpaqueBack == false )
|
|
229
233
|
this.backRect.setOpacity(0.01);
|
|
230
234
|
|
|
@@ -281,7 +285,7 @@ export class omdMetaExpression extends jsvgGroup
|
|
|
281
285
|
this.backRect.setOpacity(0.01);
|
|
282
286
|
}
|
|
283
287
|
|
|
284
|
-
makeBackgroundLight() { this.backRect.setFillColor(
|
|
288
|
+
makeBackgroundLight() { this.backRect.setFillColor( this.getBackgroundColor() ) }
|
|
285
289
|
makeBackgroundDark() { this.backRect.setFillColor( omdColor.mediumGray ) }
|
|
286
290
|
|
|
287
291
|
}
|
package/src/omdTable.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { omdColor } from "./omdColor.js";
|
|
2
|
-
import { jsvgGroup, jsvgRect, jsvgTextBox } from "@teachinglab/jsvg";
|
|
2
|
+
import { jsvgGroup, jsvgRect, jsvgTextBox, jsvgClipMask } from "@teachinglab/jsvg";
|
|
3
3
|
|
|
4
4
|
export class omdTable extends jsvgGroup
|
|
5
5
|
{
|
|
@@ -27,6 +27,22 @@ export class omdTable extends jsvgGroup
|
|
|
27
27
|
this.maxCellWidth = 300;
|
|
28
28
|
this.padding = 10;
|
|
29
29
|
|
|
30
|
+
// Background customization properties
|
|
31
|
+
this.backgroundColor = omdColor.lightGray;
|
|
32
|
+
this.backgroundCornerRadius = 15;
|
|
33
|
+
this.backgroundOpacity = 1.0;
|
|
34
|
+
this.showBackground = true;
|
|
35
|
+
|
|
36
|
+
// Alternating row color properties
|
|
37
|
+
this.alternatingRowColors = [omdColor.mediumGray, omdColor.lightGray]; // Should be an array of colors or null
|
|
38
|
+
this.headerBackgroundColor = omdColor.lightGray;
|
|
39
|
+
this.cellBackgroundColor = "white";
|
|
40
|
+
|
|
41
|
+
// Legacy properties for backward compatibility
|
|
42
|
+
this.evenRowColor = "rgba(255,255,255,0.5)";
|
|
43
|
+
this.oddRowColor = "transparent";
|
|
44
|
+
this.alternatingRowOpacity = 1.0;
|
|
45
|
+
|
|
30
46
|
this.updateLayout();
|
|
31
47
|
}
|
|
32
48
|
|
|
@@ -92,6 +108,28 @@ export class omdTable extends jsvgGroup
|
|
|
92
108
|
if ( typeof data.padding != "undefined" )
|
|
93
109
|
this.padding = data.padding;
|
|
94
110
|
|
|
111
|
+
// Load background customization properties
|
|
112
|
+
if ( typeof data.backgroundColor != "undefined" )
|
|
113
|
+
this.backgroundColor = data.backgroundColor;
|
|
114
|
+
if ( typeof data.backgroundCornerRadius != "undefined" )
|
|
115
|
+
this.backgroundCornerRadius = data.backgroundCornerRadius;
|
|
116
|
+
if ( typeof data.backgroundOpacity != "undefined" )
|
|
117
|
+
this.backgroundOpacity = data.backgroundOpacity;
|
|
118
|
+
if ( typeof data.showBackground != "undefined" )
|
|
119
|
+
this.showBackground = data.showBackground;
|
|
120
|
+
|
|
121
|
+
// Load alternating row color properties
|
|
122
|
+
if ( typeof data.alternatingRowColors != "undefined" ) {
|
|
123
|
+
this.alternatingRowColors = data.alternatingRowColors;
|
|
124
|
+
console.log('LoadFromJSON - Setting alternatingRowColors to:', this.alternatingRowColors);
|
|
125
|
+
}
|
|
126
|
+
if ( typeof data.evenRowColor != "undefined" )
|
|
127
|
+
this.evenRowColor = data.evenRowColor;
|
|
128
|
+
if ( typeof data.oddRowColor != "undefined" )
|
|
129
|
+
this.oddRowColor = data.oddRowColor;
|
|
130
|
+
if ( typeof data.alternatingRowOpacity != "undefined" )
|
|
131
|
+
this.alternatingRowOpacity = data.alternatingRowOpacity;
|
|
132
|
+
|
|
95
133
|
this.updateLayout();
|
|
96
134
|
}
|
|
97
135
|
|
|
@@ -155,6 +193,10 @@ export class omdTable extends jsvgGroup
|
|
|
155
193
|
|
|
156
194
|
updateLayout()
|
|
157
195
|
{
|
|
196
|
+
console.log('updateLayout called - alternatingRowColors:', this.alternatingRowColors);
|
|
197
|
+
console.log('alternatingRowColors type:', typeof this.alternatingRowColors);
|
|
198
|
+
console.log('alternatingRowColors isArray:', Array.isArray(this.alternatingRowColors));
|
|
199
|
+
|
|
158
200
|
this.removeAllChildren();
|
|
159
201
|
|
|
160
202
|
// If an equation is provided, generate data before measuring/layout
|
|
@@ -182,29 +224,49 @@ export class omdTable extends jsvgGroup
|
|
|
182
224
|
// without changing column widths or table background.
|
|
183
225
|
const titleBoxWidth = this.estimateTitleWidth();
|
|
184
226
|
const displayWidth = Math.max(this.width, titleBoxWidth);
|
|
185
|
-
|
|
186
|
-
// Table background with corner radius (all four corners, covers full height)
|
|
187
|
-
const tableBg = new jsvgRect();
|
|
188
|
-
tableBg.setWidthAndHeight(this.width, bodyHeight);
|
|
189
|
-
tableBg.setFillColor(omdColor.lightGray);
|
|
190
|
-
tableBg.setCornerRadius(15);
|
|
191
|
-
tableBg.setStrokeWidth(0);
|
|
192
227
|
const contentOffsetX = Math.max(0, (displayWidth - this.width) / 2);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
228
|
+
|
|
229
|
+
// Create a clipped group for rounded corners if corner radius is specified
|
|
230
|
+
let tableContentGroup;
|
|
231
|
+
if (this.backgroundCornerRadius > 0) {
|
|
232
|
+
// Create clip mask with rounded corners
|
|
233
|
+
const clipMask = new jsvgClipMask(this.width, bodyHeight, this.backgroundCornerRadius);
|
|
234
|
+
clipMask.setPosition(contentOffsetX, titleOffset);
|
|
235
|
+
this.addChild(clipMask);
|
|
236
|
+
|
|
237
|
+
// Table content will be added to the clip mask
|
|
238
|
+
tableContentGroup = clipMask;
|
|
239
|
+
|
|
240
|
+
// Create table background inside the clip mask (so it gets rounded corners)
|
|
241
|
+
if (this.showBackground) {
|
|
242
|
+
const tableBg = new jsvgRect();
|
|
243
|
+
tableBg.setWidthAndHeight(this.width, bodyHeight);
|
|
244
|
+
tableBg.setFillColor(this.backgroundColor);
|
|
245
|
+
if (this.backgroundOpacity < 1.0) {
|
|
246
|
+
tableBg.setOpacity(this.backgroundOpacity);
|
|
247
|
+
}
|
|
248
|
+
tableBg.setStrokeWidth(0);
|
|
249
|
+
tableBg.setPosition(0, 0); // Relative to clip mask
|
|
250
|
+
tableContentGroup.addChild(tableBg);
|
|
251
|
+
}
|
|
252
|
+
} else {
|
|
253
|
+
// No clipping needed, create background and use the main group
|
|
254
|
+
if (this.showBackground) {
|
|
255
|
+
const tableBg = new jsvgRect();
|
|
256
|
+
tableBg.setWidthAndHeight(this.width, bodyHeight);
|
|
257
|
+
tableBg.setFillColor(this.backgroundColor);
|
|
258
|
+
if (this.backgroundOpacity < 1.0) {
|
|
259
|
+
tableBg.setOpacity(this.backgroundOpacity);
|
|
260
|
+
}
|
|
261
|
+
tableBg.setStrokeWidth(0);
|
|
262
|
+
tableBg.setPosition(contentOffsetX, titleOffset);
|
|
263
|
+
this.addChild(tableBg);
|
|
264
|
+
}
|
|
265
|
+
tableContentGroup = this;
|
|
206
266
|
}
|
|
207
267
|
|
|
268
|
+
// Remove the separate footer rectangle since the main background now covers everything
|
|
269
|
+
|
|
208
270
|
// Generate data from equation if provided; otherwise assume valid data/headers
|
|
209
271
|
if (this.equation && this.equation.length > 0) {
|
|
210
272
|
this.generateDataFromEquation();
|
|
@@ -230,33 +292,45 @@ export class omdTable extends jsvgGroup
|
|
|
230
292
|
currentY += 30;
|
|
231
293
|
}
|
|
232
294
|
|
|
233
|
-
// Create header row
|
|
295
|
+
// Create header row
|
|
234
296
|
let currentX = 0;
|
|
297
|
+
const headerY = 0; // Relative to the clipped content group
|
|
298
|
+
|
|
299
|
+
// First create a full-width header background if using alternating colors
|
|
300
|
+
if (this.alternatingRowColors && Array.isArray(this.alternatingRowColors) && this.alternatingRowColors.length > 0) {
|
|
301
|
+
console.log('Creating header background with color:', this.alternatingRowColors[0]);
|
|
302
|
+
const headerBg = new jsvgRect();
|
|
303
|
+
headerBg.setWidthAndHeight(this.width, this.headerHeight);
|
|
304
|
+
headerBg.setFillColor(this.alternatingRowColors[0]); // Headers use first color
|
|
305
|
+
headerBg.setCornerRadius(0);
|
|
306
|
+
headerBg.setStrokeWidth(0);
|
|
307
|
+
headerBg.setPosition(0, headerY);
|
|
308
|
+
tableContentGroup.addChild(headerBg);
|
|
309
|
+
} else {
|
|
310
|
+
console.log('NOT creating header background - alternatingRowColors:', this.alternatingRowColors);
|
|
311
|
+
}
|
|
312
|
+
|
|
235
313
|
for (let col = 0; col < numCols; col++) {
|
|
236
314
|
const cellWidth = cellWidths[col];
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
headerRect.
|
|
243
|
-
} else if (col === 0) {
|
|
244
|
-
headerRect.setCornerRadius(15); // round top-left
|
|
245
|
-
} else if (col === numCols - 1) {
|
|
246
|
-
headerRect.setCornerRadius(15); // round top-right
|
|
247
|
-
} else {
|
|
315
|
+
|
|
316
|
+
// Only create individual header background if NOT using alternating colors
|
|
317
|
+
if (!this.alternatingRowColors || !Array.isArray(this.alternatingRowColors) || this.alternatingRowColors.length === 0) {
|
|
318
|
+
var headerRect = new jsvgRect();
|
|
319
|
+
headerRect.setWidthAndHeight(cellWidth, this.headerHeight);
|
|
320
|
+
headerRect.setFillColor(this.headerBackgroundColor || omdColor.lightGray);
|
|
248
321
|
headerRect.setCornerRadius(0);
|
|
322
|
+
headerRect.setStrokeWidth(0);
|
|
323
|
+
headerRect.setPosition(currentX, headerY);
|
|
324
|
+
tableContentGroup.addChild(headerRect);
|
|
249
325
|
}
|
|
250
|
-
|
|
251
|
-
headerRect.setPosition(currentX + contentOffsetX, currentY);
|
|
252
|
-
this.addChild(headerRect);
|
|
326
|
+
|
|
253
327
|
const headerText = this.createHeaderTextBox(
|
|
254
328
|
cellWidth,
|
|
255
329
|
this.headerHeight,
|
|
256
330
|
this.headers[col] || `Col ${col + 1}`
|
|
257
331
|
);
|
|
258
|
-
headerText.setPosition(currentX
|
|
259
|
-
|
|
332
|
+
headerText.setPosition(currentX, headerY);
|
|
333
|
+
tableContentGroup.addChild(headerText);
|
|
260
334
|
currentX += cellWidth;
|
|
261
335
|
}
|
|
262
336
|
currentY += this.headerHeight;
|
|
@@ -265,28 +339,37 @@ export class omdTable extends jsvgGroup
|
|
|
265
339
|
for (let row = 0; row < numRows; row++) {
|
|
266
340
|
const rowData = this.data[row];
|
|
267
341
|
let currentX = 0;
|
|
268
|
-
|
|
269
|
-
|
|
342
|
+
const rowY = this.headerHeight + row * this.cellHeight; // Relative to clipped content group
|
|
343
|
+
|
|
344
|
+
// Create row background with alternating colors if enabled
|
|
345
|
+
if (this.alternatingRowColors && Array.isArray(this.alternatingRowColors) && this.alternatingRowColors.length > 0) {
|
|
346
|
+
const colorIndex = (row + 1) % this.alternatingRowColors.length; // +1 to account for header
|
|
347
|
+
const rowColor = this.alternatingRowColors[colorIndex];
|
|
348
|
+
|
|
349
|
+
console.log(`Creating row ${row} background with color:`, rowColor, 'at position:', 0, rowY);
|
|
350
|
+
|
|
270
351
|
var barRect = new jsvgRect();
|
|
271
352
|
barRect.setWidthAndHeight(this.width, this.cellHeight);
|
|
272
|
-
barRect.setFillColor(
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
barRect.setCornerRadius(15);
|
|
276
|
-
} else {
|
|
277
|
-
barRect.setCornerRadius(0);
|
|
353
|
+
barRect.setFillColor(rowColor);
|
|
354
|
+
if (this.backgroundOpacity < 1.0) {
|
|
355
|
+
barRect.setOpacity(this.backgroundOpacity);
|
|
278
356
|
}
|
|
357
|
+
// No corner radius on individual row backgrounds - clip mask handles the rounding
|
|
358
|
+
barRect.setCornerRadius(0);
|
|
279
359
|
barRect.setStrokeWidth(0);
|
|
280
|
-
barRect.setPosition(
|
|
281
|
-
|
|
360
|
+
barRect.setPosition(0, rowY);
|
|
361
|
+
tableContentGroup.addChild(barRect);
|
|
362
|
+
} else {
|
|
363
|
+
console.log(`Row ${row}: No alternating colors - alternatingRowColors:`, this.alternatingRowColors);
|
|
282
364
|
}
|
|
365
|
+
|
|
283
366
|
for (let col = 0; col < numCols; col++) {
|
|
284
367
|
const cellWidth = cellWidths[col];
|
|
285
368
|
const cellText = this.createBodyTextBox(cellWidth, this.cellHeight, "");
|
|
286
369
|
const cellValue = rowData[col];
|
|
287
370
|
cellText.setText((cellValue ?? '').toString());
|
|
288
|
-
cellText.setPosition(currentX
|
|
289
|
-
|
|
371
|
+
cellText.setPosition(currentX, rowY);
|
|
372
|
+
tableContentGroup.addChild(cellText);
|
|
290
373
|
currentX += cellWidth;
|
|
291
374
|
}
|
|
292
375
|
currentY += this.cellHeight;
|
|
@@ -302,8 +385,8 @@ export class omdTable extends jsvgGroup
|
|
|
302
385
|
vline.setCornerRadius(0);
|
|
303
386
|
vline.setOpacity(0.5);
|
|
304
387
|
vline.setStrokeWidth(0);
|
|
305
|
-
vline.setPosition(x
|
|
306
|
-
|
|
388
|
+
vline.setPosition(x, 0); // Relative to the clipped content group
|
|
389
|
+
tableContentGroup.addChild(vline);
|
|
307
390
|
}
|
|
308
391
|
}
|
|
309
392
|
// Use displayWidth for the viewBox so the title never clips,
|
|
@@ -366,4 +449,51 @@ export class omdTable extends jsvgGroup
|
|
|
366
449
|
this.data = [];
|
|
367
450
|
this.updateLayout();
|
|
368
451
|
}
|
|
452
|
+
|
|
453
|
+
// Background customization methods
|
|
454
|
+
setBackgroundColor(color) {
|
|
455
|
+
this.backgroundColor = color;
|
|
456
|
+
this.updateLayout();
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
setBackgroundCornerRadius(radius) {
|
|
460
|
+
this.backgroundCornerRadius = radius;
|
|
461
|
+
this.updateLayout();
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
setBackgroundOpacity(opacity) {
|
|
465
|
+
this.backgroundOpacity = Math.max(0, Math.min(1, opacity));
|
|
466
|
+
this.updateLayout();
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
setShowBackground(show) {
|
|
470
|
+
this.showBackground = show;
|
|
471
|
+
this.updateLayout();
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
setBackgroundStyle(options = {}) {
|
|
475
|
+
if (options.backgroundColor !== undefined) this.backgroundColor = options.backgroundColor;
|
|
476
|
+
if (options.cornerRadius !== undefined) this.backgroundCornerRadius = options.cornerRadius;
|
|
477
|
+
if (options.opacity !== undefined) this.backgroundOpacity = Math.max(0, Math.min(1, options.opacity));
|
|
478
|
+
if (options.show !== undefined) this.showBackground = options.show;
|
|
479
|
+
this.updateLayout();
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Alternating row colors methods
|
|
483
|
+
setAlternatingRowColors(colors) {
|
|
484
|
+
console.log('setAlternatingRowColors called with:', colors);
|
|
485
|
+
this.alternatingRowColors = colors;
|
|
486
|
+
console.log('alternatingRowColors set to:', this.alternatingRowColors);
|
|
487
|
+
this.updateLayout();
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
setHeaderBackgroundColor(color) {
|
|
491
|
+
this.headerBackgroundColor = color;
|
|
492
|
+
this.updateLayout();
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
setCellBackgroundColor(color) {
|
|
496
|
+
this.cellBackgroundColor = color;
|
|
497
|
+
this.updateLayout();
|
|
498
|
+
}
|
|
369
499
|
}
|