@overtone-art/canvas-editor-core 0.3.1 → 0.3.2

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.js CHANGED
@@ -26,6 +26,7 @@ __export(index_exports, {
26
26
  CropController: () => CropController,
27
27
  DEFAULT_LAYER_SHADOW: () => DEFAULT_LAYER_SHADOW,
28
28
  DEFAULT_PATTERN_CONFIG: () => DEFAULT_PATTERN_CONFIG,
29
+ DEFAULT_SELECTION_STYLE: () => DEFAULT_SELECTION_STYLE,
29
30
  DEFAULT_TEXT_CURVE: () => DEFAULT_TEXT_CURVE,
30
31
  EventEmitter: () => EventEmitter,
31
32
  FontRegistry: () => FontRegistry,
@@ -47,7 +48,9 @@ __export(index_exports, {
47
48
  UnitConverter: () => UnitConverter,
48
49
  applyAspectLock: () => applyAspectLock,
49
50
  applyLayerShadow: () => applyLayerShadow,
51
+ applyObjectSelectionStyle: () => applyObjectSelectionStyle,
50
52
  applyPatternLocks: () => applyPatternLocks,
53
+ applySelectionStyle: () => applySelectionStyle,
51
54
  buildCurvePathData: () => buildCurvePathData,
52
55
  buildPatternDataURL: () => buildPatternDataURL,
53
56
  captureLocks: () => captureLocks,
@@ -1689,6 +1692,43 @@ function applyLayerShadow(object, config) {
1689
1692
  });
1690
1693
  }
1691
1694
 
1695
+ // src/selection-style.ts
1696
+ var DEFAULT_SELECTION_STYLE = {
1697
+ borderColor: "#c9a96e",
1698
+ cornerColor: "#c9a96e",
1699
+ cornerStrokeColor: "#101010",
1700
+ cornerSize: 11,
1701
+ cornerStyle: "circle",
1702
+ borderWidth: 1,
1703
+ borderDashArray: null,
1704
+ padding: 0,
1705
+ marqueeFill: "rgba(201, 169, 110, 0.12)"
1706
+ };
1707
+ function applyObjectSelectionStyle(object, style, zoom) {
1708
+ const scale = zoom > 0 ? 1 / zoom : 1;
1709
+ object.set({
1710
+ cornerSize: style.cornerSize * scale,
1711
+ cornerStyle: style.cornerStyle,
1712
+ cornerColor: style.cornerColor,
1713
+ cornerStrokeColor: style.cornerStrokeColor,
1714
+ // A filled handle is what makes the stroke colour visible at all.
1715
+ transparentCorners: false,
1716
+ borderColor: style.borderColor,
1717
+ borderScaleFactor: style.borderWidth * scale,
1718
+ borderDashArray: style.borderDashArray?.map((segment) => segment * scale) ?? null,
1719
+ padding: style.padding * scale
1720
+ });
1721
+ }
1722
+ function applySelectionStyle(canvas, style, zoom) {
1723
+ for (const object of canvas.getObjects()) applyObjectSelectionStyle(object, style, zoom);
1724
+ const active = canvas.getActiveObject();
1725
+ if (active) applyObjectSelectionStyle(active, style, zoom);
1726
+ canvas.selectionColor = style.marqueeFill;
1727
+ canvas.selectionBorderColor = style.borderColor;
1728
+ canvas.selectionLineWidth = style.borderWidth * (zoom > 0 ? 1 / zoom : 1);
1729
+ canvas.requestRenderAll();
1730
+ }
1731
+
1692
1732
  // src/transform.ts
1693
1733
  var SIDE_CONTROLS = ["ml", "mr", "mt", "mb"];
1694
1734
  function applyAspectLock(object, locked) {
@@ -2696,6 +2736,7 @@ var CanvasEditor = class {
2696
2736
  fileAdapter;
2697
2737
  imageProvider;
2698
2738
  zoomLevel = 1;
2739
+ selectionStyle = { ...DEFAULT_SELECTION_STYLE };
2699
2740
  mockup = null;
2700
2741
  // The design's configured background. The live canvas background is forced
2701
2742
  // transparent while a mockup preview is shown, so this is the source of truth
@@ -2743,6 +2784,7 @@ var CanvasEditor = class {
2743
2784
  this.curves = new TextCurveManager(this.canvas, this.layers, this.history, this.events);
2744
2785
  this.maskPresets = new MaskPresetManager(this.canvas, this.layers, this.history, this.events);
2745
2786
  this.setupCanvasEvents();
2787
+ this.refreshSelectionStyle();
2746
2788
  this.history.saveImmediate();
2747
2789
  this.pages = new ProjectManager(this);
2748
2790
  this.masks = new MaskController(this);
@@ -3413,6 +3455,7 @@ var CanvasEditor = class {
3413
3455
  const next = clamp(round2(level), MIN_ZOOM, MAX_ZOOM);
3414
3456
  if (next === this.zoomLevel) return;
3415
3457
  this.zoomLevel = next;
3458
+ this.refreshSelectionStyle();
3416
3459
  this.events.emit("zoom:changed", { zoom: next });
3417
3460
  }
3418
3461
  /** @deprecated use setZoom — kept for backward compatibility. */
@@ -3561,8 +3604,31 @@ var CanvasEditor = class {
3561
3604
  this.events.removeAllListeners();
3562
3605
  this.canvas.dispose();
3563
3606
  }
3607
+ // ─── Selection style ────────────────────────────────
3608
+ /** Current look of the selection frame and its handles. */
3609
+ getSelectionStyle() {
3610
+ return { ...this.selectionStyle };
3611
+ }
3612
+ /**
3613
+ * Restyles the selection frame and resize handles. Sizes are in screen
3614
+ * pixels and stay constant across zoom levels (see `SelectionStyle`).
3615
+ */
3616
+ setSelectionStyle(style) {
3617
+ this.selectionStyle = { ...this.selectionStyle, ...style };
3618
+ this.refreshSelectionStyle();
3619
+ }
3620
+ refreshSelectionStyle() {
3621
+ applySelectionStyle(this.canvas, this.selectionStyle, this.zoomLevel);
3622
+ }
3564
3623
  // ─── Private ────────────────────────────────────────
3565
3624
  setupCanvasEvents() {
3625
+ this.canvas.on("object:added", (e) => {
3626
+ if (e.target) applyObjectSelectionStyle(e.target, this.selectionStyle, this.zoomLevel);
3627
+ });
3628
+ const styleActive = () => {
3629
+ const active = this.canvas.getActiveObject();
3630
+ if (active) applyObjectSelectionStyle(active, this.selectionStyle, this.zoomLevel);
3631
+ };
3566
3632
  this.canvas.on("object:modified", (e) => {
3567
3633
  if (!e.target) return;
3568
3634
  const layer = this.layers.findByObject(e.target);
@@ -3572,10 +3638,12 @@ var CanvasEditor = class {
3572
3638
  }
3573
3639
  });
3574
3640
  this.canvas.on("selection:created", (e) => {
3641
+ styleActive();
3575
3642
  const selected = (e.selected ?? []).map((obj) => this.layers.findByObject(obj)?.id).filter((id) => id !== void 0);
3576
3643
  this.events.emit("selection:changed", { selected });
3577
3644
  });
3578
3645
  this.canvas.on("selection:updated", (e) => {
3646
+ styleActive();
3579
3647
  const selected = (e.selected ?? []).map((obj) => this.layers.findByObject(obj)?.id).filter((id) => id !== void 0);
3580
3648
  this.events.emit("selection:changed", { selected });
3581
3649
  });
@@ -3657,6 +3725,7 @@ var AnnotationOverlay = class {
3657
3725
  CropController,
3658
3726
  DEFAULT_LAYER_SHADOW,
3659
3727
  DEFAULT_PATTERN_CONFIG,
3728
+ DEFAULT_SELECTION_STYLE,
3660
3729
  DEFAULT_TEXT_CURVE,
3661
3730
  EventEmitter,
3662
3731
  FontRegistry,
@@ -3678,7 +3747,9 @@ var AnnotationOverlay = class {
3678
3747
  UnitConverter,
3679
3748
  applyAspectLock,
3680
3749
  applyLayerShadow,
3750
+ applyObjectSelectionStyle,
3681
3751
  applyPatternLocks,
3752
+ applySelectionStyle,
3682
3753
  buildCurvePathData,
3683
3754
  buildPatternDataURL,
3684
3755
  captureLocks,