@tsdraw/core 0.8.4 → 0.9.0

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/dist/index.cjs CHANGED
@@ -249,8 +249,13 @@ function setViewport(viewport, updater) {
249
249
  function panViewport(viewport, dx, dy) {
250
250
  return { ...viewport, x: viewport.x + dx, y: viewport.y + dy };
251
251
  }
252
- function zoomViewport(viewport, factor, centerX, centerY) {
253
- const zoom = Math.max(0.1, Math.min(4, viewport.zoom * factor));
252
+ var DEFAULT_ZOOM_RANGE = { min: 0.1, max: 4 };
253
+ function clampZoom(zoom, range) {
254
+ const { min, max } = range ?? DEFAULT_ZOOM_RANGE;
255
+ return Math.max(min, Math.min(max, zoom));
256
+ }
257
+ function zoomViewport(viewport, factor, centerX, centerY, zoomRange) {
258
+ const zoom = clampZoom(viewport.zoom * factor, zoomRange);
254
259
  if (centerX == null || centerY == null) {
255
260
  return { ...viewport, zoom };
256
261
  }
@@ -305,7 +310,8 @@ var SLIDE_FRICTION = 0.92;
305
310
  var SLIDE_MIN_SPEED = 0.01;
306
311
  var SLIDE_MAX_SPEED = 2;
307
312
  var SLIDE_MIN_VELOCITY_TO_START = 0.1;
308
- function startCameraSlide(session, applyPan, onFrame) {
313
+ function startCameraSlide(session, applyPan, onFrame, slideOptions) {
314
+ const friction = slideOptions?.friction ?? SLIDE_FRICTION;
309
315
  const timeSinceLastMove = performance.now() - session.lastMoveTime;
310
316
  const FRAME_DURATION = 16;
311
317
  const decayFactor = Math.pow(1 - VELOCITY_LERP_FACTOR, timeSinceLastMove / FRAME_DURATION);
@@ -325,7 +331,7 @@ function startCameraSlide(session, applyPan, onFrame) {
325
331
  lastTime = now;
326
332
  applyPan(dirX * currentSpeed * elapsed, dirY * currentSpeed * elapsed);
327
333
  onFrame();
328
- currentSpeed *= SLIDE_FRICTION;
334
+ currentSpeed *= friction;
329
335
  if (currentSpeed < SLIDE_MIN_SPEED) {
330
336
  rafId = 0;
331
337
  return;
@@ -366,9 +372,12 @@ function resolveThemeColor(colorStyle, theme) {
366
372
  return DARK_COLORS[colorStyle] ?? lightThemeColor;
367
373
  }
368
374
  var CanvasRenderer = class {
369
- theme = "light";
375
+ _theme = "light";
376
+ get theme() {
377
+ return this._theme;
378
+ }
370
379
  setTheme(theme) {
371
- this.theme = theme;
380
+ this._theme = theme;
372
381
  }
373
382
  render(ctx, viewport, shapes) {
374
383
  ctx.save();
@@ -386,7 +395,7 @@ var CanvasRenderer = class {
386
395
  const width = (STROKE_WIDTHS[shape.props.size] ?? 3.5) * shape.props.scale;
387
396
  const samples = flattenSegments(shape);
388
397
  if (samples.length === 0) return;
389
- const color = resolveThemeColor(shape.props.color, this.theme);
398
+ const color = resolveThemeColor(shape.props.color, this._theme);
390
399
  const fillStyle = shape.props.fill ?? "none";
391
400
  if (shape.props.isClosed && fillStyle !== "none") {
392
401
  this.paintClosedShapeFill(ctx, samples, color, fillStyle);
@@ -452,7 +461,7 @@ var CanvasRenderer = class {
452
461
  ctx.fillStyle = color;
453
462
  ctx.globalAlpha = 0.55;
454
463
  } else if (fillStyle === "none") {
455
- ctx.fillStyle = this.theme === "dark" ? "#0f0f0f" : "#fafafa";
464
+ ctx.fillStyle = this._theme === "dark" ? "#0f0f0f" : "#fafafa";
456
465
  ctx.globalAlpha = 1;
457
466
  } else {
458
467
  ctx.fillStyle = color;
@@ -548,6 +557,102 @@ function getLineDash(dash, width) {
548
557
  }
549
558
  }
550
559
 
560
+ // src/canvas/backgroundRenderer.ts
561
+ var DEFAULT_SPACING = 20;
562
+ var DEFAULT_LINE_WIDTH = 0.5;
563
+ var DEFAULT_DOT_RADIUS = 1;
564
+ var DEFAULT_OPACITY = 0.25;
565
+ function resolvePresetPatternColor(colorLight, colorDark, theme) {
566
+ if (theme === "dark") return colorDark ?? colorLight ?? "#888888";
567
+ return colorLight ?? "#c0c0c0";
568
+ }
569
+ function visiblePageRect(viewport, canvasWidth, canvasHeight) {
570
+ return {
571
+ minX: (0 - viewport.x) / viewport.zoom,
572
+ minY: (0 - viewport.y) / viewport.zoom,
573
+ maxX: (canvasWidth - viewport.x) / viewport.zoom,
574
+ maxY: (canvasHeight - viewport.y) / viewport.zoom
575
+ };
576
+ }
577
+ function drawHorizontalLines(ctx, visible, spacing, lineWidth, color, opacity) {
578
+ const startY = Math.floor(visible.minY / spacing) * spacing;
579
+ ctx.save();
580
+ ctx.strokeStyle = color;
581
+ ctx.lineWidth = lineWidth / ctx.getTransform().a;
582
+ ctx.globalAlpha = opacity;
583
+ ctx.beginPath();
584
+ for (let y = startY; y <= visible.maxY; y += spacing) {
585
+ ctx.moveTo(visible.minX, y);
586
+ ctx.lineTo(visible.maxX, y);
587
+ }
588
+ ctx.stroke();
589
+ ctx.restore();
590
+ }
591
+ function drawGridLines(ctx, visible, spacing, lineWidth, color, opacity) {
592
+ const startX = Math.floor(visible.minX / spacing) * spacing;
593
+ const startY = Math.floor(visible.minY / spacing) * spacing;
594
+ const compensatedWidth = lineWidth / ctx.getTransform().a;
595
+ ctx.save();
596
+ ctx.strokeStyle = color;
597
+ ctx.lineWidth = compensatedWidth;
598
+ ctx.globalAlpha = opacity;
599
+ ctx.beginPath();
600
+ for (let x = startX; x <= visible.maxX; x += spacing) {
601
+ ctx.moveTo(x, visible.minY);
602
+ ctx.lineTo(x, visible.maxY);
603
+ }
604
+ for (let y = startY; y <= visible.maxY; y += spacing) {
605
+ ctx.moveTo(visible.minX, y);
606
+ ctx.lineTo(visible.maxX, y);
607
+ }
608
+ ctx.stroke();
609
+ ctx.restore();
610
+ }
611
+ function drawDotPattern(ctx, visible, spacing, dotRadius, color, opacity) {
612
+ const startX = Math.floor(visible.minX / spacing) * spacing;
613
+ const startY = Math.floor(visible.minY / spacing) * spacing;
614
+ const compensatedRadius = dotRadius / ctx.getTransform().a;
615
+ ctx.save();
616
+ ctx.fillStyle = color;
617
+ ctx.globalAlpha = opacity;
618
+ ctx.beginPath();
619
+ for (let x = startX; x <= visible.maxX; x += spacing) {
620
+ for (let y = startY; y <= visible.maxY; y += spacing) {
621
+ ctx.moveTo(x + compensatedRadius, y);
622
+ ctx.arc(x, y, compensatedRadius, 0, Math.PI * 2);
623
+ }
624
+ }
625
+ ctx.fill();
626
+ ctx.restore();
627
+ }
628
+ function renderCanvasBackground(ctx, viewport, canvasWidth, canvasHeight, options, theme) {
629
+ if (!options || options.type === "blank") return;
630
+ if (options.type === "custom") {
631
+ options.render(ctx, viewport, canvasWidth, canvasHeight);
632
+ return;
633
+ }
634
+ const spacing = options.spacing ?? DEFAULT_SPACING;
635
+ if (spacing <= 0) return;
636
+ const color = resolvePresetPatternColor(options.color, options.colorDark, theme);
637
+ const opacity = options.opacity ?? DEFAULT_OPACITY;
638
+ const visible = visiblePageRect(viewport, canvasWidth, canvasHeight);
639
+ ctx.save();
640
+ ctx.translate(viewport.x, viewport.y);
641
+ ctx.scale(viewport.zoom, viewport.zoom);
642
+ switch (options.type) {
643
+ case "lines":
644
+ drawHorizontalLines(ctx, visible, spacing, options.size ?? DEFAULT_LINE_WIDTH, color, opacity);
645
+ break;
646
+ case "grid":
647
+ drawGridLines(ctx, visible, spacing, options.size ?? DEFAULT_LINE_WIDTH, color, opacity);
648
+ break;
649
+ case "dots":
650
+ drawDotPattern(ctx, visible, spacing, options.size ?? DEFAULT_DOT_RADIUS, color, opacity);
651
+ break;
652
+ }
653
+ ctx.restore();
654
+ }
655
+
551
656
  // src/input/inputManager.ts
552
657
  var InputManager = class {
553
658
  _current = { x: 0, y: 0 };
@@ -1711,7 +1816,7 @@ var Editor = class {
1711
1816
  historyBatchChanged = false;
1712
1817
  // Creates a new editor instance with the given options (with defaults if not provided)
1713
1818
  constructor(opts = {}) {
1714
- this.options = { dragDistanceSquared: opts.dragDistanceSquared ?? DRAG_DISTANCE_SQUARED };
1819
+ this.options = { dragDistanceSquared: opts.dragDistanceSquared ?? DRAG_DISTANCE_SQUARED, zoomRange: opts.zoomRange };
1715
1820
  this.lastDocumentSnapshot = this.getDocumentSnapshot();
1716
1821
  this.store.listen(() => {
1717
1822
  this.captureDocumentHistory();
@@ -1825,7 +1930,7 @@ var Editor = class {
1825
1930
  this.viewport = {
1826
1931
  x: partial.x ?? this.viewport.x,
1827
1932
  y: partial.y ?? this.viewport.y,
1828
- zoom: Math.max(0.1, Math.min(4, rawZoom))
1933
+ zoom: clampZoom(rawZoom, this.options.zoomRange)
1829
1934
  };
1830
1935
  this.emitChange();
1831
1936
  }
@@ -1836,7 +1941,7 @@ var Editor = class {
1836
1941
  });
1837
1942
  }
1838
1943
  zoomAt(factor, screenX, screenY) {
1839
- this.viewport = zoomViewport(this.viewport, factor, screenX, screenY);
1944
+ this.viewport = zoomViewport(this.viewport, factor, screenX, screenY, this.options.zoomRange);
1840
1945
  this.emitChange();
1841
1946
  }
1842
1947
  deleteShapes(ids) {
@@ -2260,6 +2365,7 @@ exports.CanvasRenderer = CanvasRenderer;
2260
2365
  exports.CircleDrawingState = CircleDrawingState;
2261
2366
  exports.CircleIdleState = CircleIdleState;
2262
2367
  exports.DEFAULT_COLORS = DEFAULT_COLORS;
2368
+ exports.DEFAULT_ZOOM_RANGE = DEFAULT_ZOOM_RANGE;
2263
2369
  exports.DRAG_DISTANCE_SQUARED = DRAG_DISTANCE_SQUARED;
2264
2370
  exports.DocumentStore = DocumentStore;
2265
2371
  exports.ERASER_MARGIN = ERASER_MARGIN;
@@ -2288,6 +2394,7 @@ exports.boundsIntersect = boundsIntersect;
2288
2394
  exports.boundsOf = boundsOf;
2289
2395
  exports.buildStartPositions = buildStartPositions;
2290
2396
  exports.buildTransformSnapshots = buildTransformSnapshots;
2397
+ exports.clampZoom = clampZoom;
2291
2398
  exports.closestOnSegment = closestOnSegment;
2292
2399
  exports.createViewport = createViewport;
2293
2400
  exports.decodeFirstPoint = decodeFirstPoint;
@@ -2310,6 +2417,7 @@ exports.pageToScreen = pageToScreen;
2310
2417
  exports.panViewport = panViewport;
2311
2418
  exports.pointHitsShape = pointHitsShape;
2312
2419
  exports.recordsToDocumentSnapshot = recordsToDocumentSnapshot;
2420
+ exports.renderCanvasBackground = renderCanvasBackground;
2313
2421
  exports.resolveThemeColor = resolveThemeColor;
2314
2422
  exports.rotatePoint = rotatePoint;
2315
2423
  exports.screenToPage = screenToPage;