@teachinglab/omd 0.7.32 → 0.7.34

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.
@@ -24,6 +24,8 @@ export class omdDisplay {
24
24
  centerContent: true,
25
25
  topMargin: 40,
26
26
  bottomMargin: 16,
27
+ observeResize: true,
28
+ expandViewboxToFitContent: true,
27
29
  fitToContent: true, // Fit to content size by default
28
30
  autoScale: false, // Don't auto-scale by default
29
31
  maxScale: 1, // Do not upscale beyond 1 by default
@@ -76,7 +78,7 @@ export class omdDisplay {
76
78
  }
77
79
 
78
80
  // Handle resize
79
- if (window.ResizeObserver) {
81
+ if (this.options.observeResize && window.ResizeObserver) {
80
82
  this.resizeObserver = new ResizeObserver(() => {
81
83
  this._handleResize();
82
84
  });
@@ -103,6 +105,13 @@ export class omdDisplay {
103
105
  if (this.options.debugExtents) this._drawDebugOverlays();
104
106
  }
105
107
 
108
+ _syncViewboxToContainer(width = this.container.offsetWidth || 0, height = this.container.offsetHeight || 0) {
109
+ if (!width || !height) return;
110
+ this.svg.setViewbox(width, height);
111
+ this.svg.setWidthAndHeight(width, height);
112
+ this._lastViewbox = `0 0 ${width} ${height}`;
113
+ }
114
+
106
115
  /**
107
116
  * Ensure the internal SVG viewBox is at least as large as the provided content dimensions.
108
117
  * This prevents clipping when content is larger than the current viewBox.
@@ -573,6 +582,11 @@ export class omdDisplay {
573
582
  // prefer expanding the SVG viewBox instead of moving nodes.
574
583
  const scaledWidthFinal = contentWidth * scale;
575
584
  const scaledHeightFinal = contentHeight * scale;
585
+ const availableHeight = Math.max(
586
+ 0,
587
+ containerHeight - (this.options.topMargin || 0) - (this.options.bottomMargin || 0)
588
+ );
589
+ const y = (this.options.topMargin || 0) + Math.max(0, (availableHeight - scaledHeightFinal) / 2);
576
590
  const totalNeededH = scaledHeightFinal + (this.options.topMargin || 0) + (this.options.bottomMargin || 0);
577
591
 
578
592
  const willOverflowHoriz = scaledWidthFinal > containerWidth;
@@ -582,15 +596,18 @@ export class omdDisplay {
582
596
  const contentSig = `${contentWidth}x${contentHeight}x${scale}`;
583
597
  if (this._lastCenterSignature === contentSig && !willOverflowHoriz && !willOverflowVert) {
584
598
  // Only update position; skip expensive ensureViewboxFits
585
- if (this.node.setPosition) this.node.setPosition(x, this.options.topMargin);
599
+ if (this.node.setPosition) this.node.setPosition(x, y);
586
600
  return;
587
601
  }
588
602
 
589
603
  if (willOverflowHoriz || willOverflowVert) {
590
604
  // Set scale but do NOT reposition node (preserve its absolute positions).
591
605
  if (this.node.setScale) this.node.setScale(scale);
592
- // Expand viewBox to contain entire unscaled content so nothing is clipped.
593
- this._ensureViewboxFits(contentWidth, contentHeight);
606
+ if (this.options.expandViewboxToFitContent !== false) {
607
+ this._ensureViewboxFits(contentWidth, contentHeight);
608
+ } else {
609
+ this._syncViewboxToContainer(containerWidth, containerHeight);
610
+ }
594
611
  // Reposition overlay toolbar in case viewBox/container changed
595
612
  this._repositionOverlayToolbar();
596
613
 
@@ -603,14 +620,16 @@ export class omdDisplay {
603
620
  }
604
621
  if (this.options.debugExtents) this._drawDebugOverlays();
605
622
  } else {
606
- // Y is top margin; scaled content will grow downward
607
- this.node.setPosition(x, this.options.topMargin);
623
+ this.node.setPosition(x, y);
608
624
 
609
625
  // Reposition overlay toolbar (if any)
610
626
  this._repositionOverlayToolbar();
611
627
 
612
- // Ensure viewBox can contain the (unscaled) content to avoid clipping in some hosts
613
- this._ensureViewboxFits(contentWidth, contentHeight);
628
+ if (this.options.expandViewboxToFitContent !== false) {
629
+ this._ensureViewboxFits(contentWidth, contentHeight);
630
+ } else {
631
+ this._syncViewboxToContainer(containerWidth, containerHeight);
632
+ }
614
633
 
615
634
  if (totalNeededH > containerHeight) {
616
635
  // Let the host scroll vertically; keep horizontal overflow hidden to avoid layout shift
@@ -822,10 +841,13 @@ export class omdDisplay {
822
841
  // Ensure overlay toolbar is positioned initially
823
842
  this._repositionOverlayToolbar();
824
843
 
825
- // Also ensure the viewBox is large enough to contain the node (avoid clipping)
826
- const cw = (this.node && this.node.width) ? this.node.width : 0;
827
- const ch = (this.node && this.node.height) ? this.node.height : 0;
828
- this._ensureViewboxFits(cw, ch);
844
+ if (this.options.expandViewboxToFitContent !== false) {
845
+ const cw = (this.node && this.node.width) ? this.node.width : 0;
846
+ const ch = (this.node && this.node.height) ? this.node.height : 0;
847
+ this._ensureViewboxFits(cw, ch);
848
+ } else {
849
+ this._syncViewboxToContainer();
850
+ }
829
851
 
830
852
  if (this.options.debugExtents) this._drawDebugOverlays();
831
853
  else this._clearDebugOverlays();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teachinglab/omd",
3
- "version": "0.7.32",
3
+ "version": "0.7.34",
4
4
  "description": "omd",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",
@@ -55,6 +55,10 @@ export class omdCoordinatePlane extends jsvgGroup {
55
55
  this.variables = [];
56
56
  this.basePaddingBottom = 30;
57
57
  this.interactivePaddingBottom = 0;
58
+ this.customPaddingTop = null;
59
+ this.customPaddingRight = null;
60
+ this.customPaddingBottom = null;
61
+ this.customPaddingLeft = null;
58
62
  this.renderedFunctionEntries = [];
59
63
  this.sliderControlRows = [];
60
64
 
@@ -87,12 +91,29 @@ export class omdCoordinatePlane extends jsvgGroup {
87
91
  this.forceAllTickLabels = (data.forceAllTickLabels !== undefined) ? data.forceAllTickLabels : this.forceAllTickLabels;
88
92
  this.showTickLabels = (data.showTickLabels !== undefined) ? data.showTickLabels : this.showTickLabels;
89
93
 
94
+ if (typeof data.graphPadding === "number") {
95
+ this.customPaddingTop = data.graphPadding;
96
+ this.customPaddingRight = data.graphPadding;
97
+ this.customPaddingBottom = data.graphPadding;
98
+ this.customPaddingLeft = data.graphPadding;
99
+ } else if (data.graphPadding && typeof data.graphPadding === "object") {
100
+ if (typeof data.graphPadding.top === "number") this.customPaddingTop = data.graphPadding.top;
101
+ if (typeof data.graphPadding.right === "number") this.customPaddingRight = data.graphPadding.right;
102
+ if (typeof data.graphPadding.bottom === "number") this.customPaddingBottom = data.graphPadding.bottom;
103
+ if (typeof data.graphPadding.left === "number") this.customPaddingLeft = data.graphPadding.left;
104
+ }
105
+
106
+ if (typeof data.paddingTop === "number") this.customPaddingTop = data.paddingTop;
107
+ if (typeof data.paddingRight === "number") this.customPaddingRight = data.paddingRight;
108
+ if (typeof data.paddingBottom === "number") this.customPaddingBottom = data.paddingBottom;
109
+ if (typeof data.paddingLeft === "number") this.customPaddingLeft = data.paddingLeft;
110
+
90
111
  // Load background customization properties
91
112
  this.backgroundColor = data.backgroundColor || this.backgroundColor;
92
113
  this.backgroundCornerRadius = (data.backgroundCornerRadius !== undefined) ? data.backgroundCornerRadius : this.backgroundCornerRadius;
93
114
  this.backgroundOpacity = (data.backgroundOpacity !== undefined) ? data.backgroundOpacity : this.backgroundOpacity;
94
115
  this.showBackground = (data.showBackground !== undefined) ? data.showBackground : this.showBackground;
95
- this.interactive = data.interactive === true;
116
+ this.interactive = data.interactive === true || data.interactive === "true";
96
117
  this.variables = this.normalizeInteractiveVariables(
97
118
  data.variables || data.interactiveVariables || []
98
119
  );
@@ -167,7 +188,7 @@ export class omdCoordinatePlane extends jsvgGroup {
167
188
 
168
189
  this.sliderControlRows = [];
169
190
  if (this.shouldShowInteractiveControls()) {
170
- this.createInteractiveControls(contentGroup.svgObject);
191
+ this.createInteractiveControls(this.svgObject);
171
192
  }
172
193
  }
173
194
 
@@ -295,14 +316,19 @@ export class omdCoordinatePlane extends jsvgGroup {
295
316
  // ===== Helper functions =====
296
317
 
297
318
  calculatePadding() {
298
- this.paddingLeft = this.yLabel ? 50 : 30;
299
- this.basePaddingBottom = this.xLabel ? 50 : 30;
319
+ const defaultPaddingLeft = this.yLabel ? 50 : 30;
320
+ const defaultPaddingBottom = this.xLabel ? 50 : 30;
321
+ const defaultPaddingTop = 25;
322
+ const defaultPaddingRight = 25;
323
+
324
+ this.paddingLeft = typeof this.customPaddingLeft === "number" ? this.customPaddingLeft : defaultPaddingLeft;
325
+ this.basePaddingBottom = typeof this.customPaddingBottom === "number" ? this.customPaddingBottom : defaultPaddingBottom;
300
326
  this.interactivePaddingBottom = this.shouldShowInteractiveControls()
301
327
  ? this.getInteractiveControlHeight()
302
328
  : 0;
303
329
  this.paddingBottom = this.basePaddingBottom + this.interactivePaddingBottom;
304
- this.paddingTop = 25;
305
- this.paddingRight = 25;
330
+ this.paddingTop = typeof this.customPaddingTop === "number" ? this.customPaddingTop : defaultPaddingTop;
331
+ this.paddingRight = typeof this.customPaddingRight === "number" ? this.customPaddingRight : defaultPaddingRight;
306
332
  }
307
333
 
308
334
  computeAxisPos(isXAxis, value) {