@teachinglab/omd 0.1.8 → 0.1.10

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.
@@ -22,25 +22,25 @@ export class omdDisplay {
22
22
  edgePadding: 16, // Horizontal padding from edges when scaling
23
23
  ...options
24
24
  };
25
-
25
+
26
26
  // Create SVG container
27
27
  this.svg = new jsvgContainer();
28
28
  this.node = null;
29
-
29
+
30
30
  // Set up the SVG
31
31
  this._setupSVG();
32
32
  }
33
-
33
+
34
34
  _setupSVG() {
35
35
  const width = this.container.offsetWidth || 800;
36
36
  const height = this.container.offsetHeight || 600;
37
-
37
+
38
38
  this.svg.setViewbox(width, height);
39
39
  this.svg.svgObject.style.verticalAlign = "middle";
40
40
  // Enable internal scrolling via native SVG scrolling if content overflows
41
41
  this.svg.svgObject.style.overflow = 'hidden';
42
42
  this.container.appendChild(this.svg.svgObject);
43
-
43
+
44
44
  // Handle resize
45
45
  if (window.ResizeObserver) {
46
46
  this.resizeObserver = new ResizeObserver(() => {
@@ -49,12 +49,12 @@ export class omdDisplay {
49
49
  this.resizeObserver.observe(this.container);
50
50
  }
51
51
  }
52
-
52
+
53
53
  _handleResize() {
54
54
  const width = this.container.offsetWidth;
55
55
  const height = this.container.offsetHeight;
56
56
  this.svg.setViewbox(width, height);
57
-
57
+
58
58
  if (this.options.centerContent && this.node) {
59
59
  this.centerNode();
60
60
  }
@@ -62,7 +62,7 @@ export class omdDisplay {
62
62
  // Reposition overlay toolbar (if any) on resize
63
63
  this._repositionOverlayToolbar();
64
64
  }
65
-
65
+
66
66
  centerNode() {
67
67
  if (!this.node) return;
68
68
  const containerWidth = this.container.offsetWidth || 0;
@@ -140,26 +140,26 @@ export class omdDisplay {
140
140
  this.container.style.overflow = 'hidden';
141
141
  }
142
142
  }
143
-
143
+
144
144
  fitToContent() {
145
145
  if (!this.node) {
146
146
  return;
147
147
  }
148
-
148
+
149
149
  // Try to get actual rendered dimensions
150
150
  let actualWidth = 0;
151
151
  let actualHeight = 0;
152
-
152
+
153
153
  // Get both sequence and current step dimensions
154
154
  let sequenceWidth = 0, sequenceHeight = 0;
155
155
  let stepWidth = 0, stepHeight = 0;
156
-
156
+
157
157
  if (this.node.getSequence) {
158
158
  const sequence = this.node.getSequence();
159
159
  if (sequence && sequence.width && sequence.height) {
160
160
  sequenceWidth = sequence.width;
161
161
  sequenceHeight = sequence.height;
162
-
162
+
163
163
  // Check current step dimensions too
164
164
  if (sequence.getCurrentStep) {
165
165
  const currentStep = sequence.getCurrentStep();
@@ -168,39 +168,39 @@ export class omdDisplay {
168
168
  stepHeight = currentStep.height;
169
169
  }
170
170
  }
171
-
171
+
172
172
  // Use the larger of sequence or step dimensions
173
173
  actualWidth = Math.max(sequenceWidth, stepWidth);
174
174
  actualHeight = Math.max(sequenceHeight, stepHeight);
175
175
  }
176
176
  }
177
-
177
+
178
178
  // Fallback to node dimensions only if sequence/step dimensions aren't available
179
179
  if ((actualWidth === 0 || actualHeight === 0) && this.node.width && this.node.height) {
180
180
  actualWidth = this.node.width;
181
181
  actualHeight = this.node.height;
182
182
  }
183
-
183
+
184
184
  // Fallback dimensions
185
185
  if (actualWidth === 0 || actualHeight === 0) {
186
186
  actualWidth = 200;
187
187
  actualHeight = 60;
188
188
  }
189
-
189
+
190
190
  const padding = 10; // More comfortable padding to match user expectation
191
191
  const newWidth = actualWidth + (padding * 2);
192
192
  const newHeight = actualHeight + (padding * 2);
193
-
194
-
193
+
194
+
195
195
  // Position the content at the minimal padding offset FIRST
196
196
  if (this.node && this.node.setPosition) {
197
197
  this.node.setPosition(padding, padding);
198
198
  }
199
-
199
+
200
200
  // Update SVG dimensions with viewBox starting from 0,0 since we repositioned content
201
201
  this.svg.setViewbox(newWidth, newHeight);
202
202
  this.svg.setWidthAndHeight(newWidth, newHeight);
203
-
203
+
204
204
  // Update container
205
205
  this.container.style.width = `${newWidth}px`;
206
206
  this.container.style.height = `${newHeight}px`;
@@ -216,7 +216,7 @@ export class omdDisplay {
216
216
  if (this.node) {
217
217
  this.svg.removeChild(this.node);
218
218
  }
219
-
219
+
220
220
  // Create node from expression
221
221
  if (typeof expression === 'string') {
222
222
  if (expression.includes(';')) {
@@ -241,7 +241,7 @@ export class omdDisplay {
241
241
  // Assume it's already a node
242
242
  this.node = expression;
243
243
  }
244
-
244
+
245
245
  // Initialize and render
246
246
  const sequence = this.node.getSequence ? this.node.getSequence() : null;
247
247
  if (sequence) {
@@ -250,7 +250,7 @@ export class omdDisplay {
250
250
  sequence.updateStepsVisibility(step => (step.stepMark ?? 0) === sequence.getFilterLevel());
251
251
  }
252
252
  this.svg.addChild(this.node);
253
-
253
+
254
254
  // Apply any stored font settings
255
255
  if (this.options.fontFamily) {
256
256
  // Small delay to ensure SVG elements are fully rendered
@@ -258,7 +258,7 @@ export class omdDisplay {
258
258
  this.setFont(this.options.fontFamily, this.options.fontWeight || '400');
259
259
  }, 10);
260
260
  }
261
-
261
+
262
262
  // Only use fitToContent for tight sizing when explicitly requested
263
263
  if (this.options.fitToContent) {
264
264
  this.fitToContent();
@@ -267,7 +267,7 @@ export class omdDisplay {
267
267
  }
268
268
  // Ensure overlay toolbar is positioned initially
269
269
  this._repositionOverlayToolbar();
270
-
270
+
271
271
  // Provide a default global refresh function if not present
272
272
  if (typeof window !== 'undefined' && !window.refreshDisplayAndFilters) {
273
273
  window.refreshDisplayAndFilters = () => {
@@ -293,10 +293,42 @@ export class omdDisplay {
293
293
  }
294
294
  };
295
295
  }
296
-
296
+
297
297
  return this.node;
298
298
  }
299
-
299
+
300
+ /**
301
+ * Add a jsvg child to the internal SVG container and optionally
302
+ * trigger layout/centering.
303
+ * @param {object} child - A jsvg node to add
304
+ */
305
+ addChild(child) {
306
+ this.svg.addChild(child);
307
+
308
+ if (this.options.centerContent) this.centerNode();
309
+
310
+ return child;
311
+ }
312
+
313
+ /**
314
+ * Remove a child previously added to the internal SVG container.
315
+ * @param {object} child
316
+ */
317
+ removeChild(child) {
318
+ if (!this.svg) return;
319
+ try {
320
+ if (typeof this.svg.removeChild === 'function') {
321
+ this.svg.removeChild(child);
322
+ } else if (child && child.svgObject && this.svg.svgObject && this.svg.svgObject.contains(child.svgObject)) {
323
+ this.svg.svgObject.removeChild(child.svgObject);
324
+ }
325
+ } catch (e) {
326
+ // no-op
327
+ }
328
+ // If the removed child was the main node, clear reference
329
+ if (this.node === child) this.node = null;
330
+ }
331
+
300
332
  /**
301
333
  * Updates the display with a new node
302
334
  * @param {omdNode} newNode - The new node to display
@@ -305,19 +337,19 @@ export class omdDisplay {
305
337
  if (this.node) {
306
338
  this.svg.removeChild(this.node);
307
339
  }
308
-
340
+
309
341
  this.node = newNode;
310
342
  this.node.setFontSize(this.options.fontSize);
311
343
  this.node.initialize();
312
344
  this.svg.addChild(this.node);
313
-
345
+
314
346
  if (this.options.centerContent) {
315
347
  this.centerNode();
316
348
  }
317
349
  // Ensure overlay toolbar is positioned on updates
318
350
  this._repositionOverlayToolbar();
319
351
  }
320
-
352
+
321
353
  /**
322
354
  * Gets the current node
323
355
  * @returns {omdNode|null} The current node
@@ -345,7 +377,7 @@ export class omdDisplay {
345
377
  node.positionToolbarOverlay(containerWidth, containerHeight, 16);
346
378
  }
347
379
  }
348
-
380
+
349
381
  /**
350
382
  * Sets the font size
351
383
  * @param {number} size - The font size
@@ -367,7 +399,7 @@ export class omdDisplay {
367
399
  }
368
400
  }
369
401
  }
370
-
402
+
371
403
  /**
372
404
  * Sets the font family for all elements in the display
373
405
  * @param {string} fontFamily - CSS font-family string (e.g., '"Shantell Sans", cursive')
@@ -383,16 +415,16 @@ export class omdDisplay {
383
415
  // Recursively apply to all children
384
416
  Array.from(element.children || []).forEach(applyFont);
385
417
  };
386
-
418
+
387
419
  // Apply font to the entire SVG
388
420
  applyFont(this.svg.svgObject);
389
-
421
+
390
422
  // Store font settings for future use
391
423
  this.options.fontFamily = fontFamily;
392
424
  this.options.fontWeight = fontWeight;
393
425
  }
394
426
  }
395
-
427
+
396
428
  /**
397
429
  * Clears the display
398
430
  */
@@ -402,7 +434,7 @@ export class omdDisplay {
402
434
  this.node = null;
403
435
  }
404
436
  }
405
-
437
+
406
438
  /**
407
439
  * Destroys the renderer and cleans up resources
408
440
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teachinglab/omd",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "omd",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",
@@ -103,11 +103,8 @@ export class omdBalanceHanger extends jsvgGroup
103
103
  const displayWidth = Math.max(300, contentWidth + padding);
104
104
  const displayHeight = Math.max(200, contentHeight + padding);
105
105
  this.setWidthAndHeight(displayWidth, displayHeight);
106
- // Store desired viewBox on the group so the wrapper <svg> can use it
107
- // Centered on (0,0) to accommodate negative/positive coordinates used above
108
106
  this.svgObject.setAttribute("viewBox", `${-displayWidth/2} ${-displayHeight/2} ${displayWidth} ${displayHeight}`);
109
- // Ensure no extra offset; content already drawn around origin
110
- this.setPosition(0, 0);
107
+ this.setPosition(-displayWidth / 2, -displayHeight / 2);
111
108
  }
112
109
 
113
110
  makeValueStack( values, xOffset, yOffset )