@linkurious/ogma-annotations 2.0.3 → 2.1.1

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.
@@ -1 +1 @@
1
- :root{--handle-width: 2px;--point-handle-width: calc(3 * var(--handle-width));--point-handle-border: 2px;--point-handle-total: calc(var(--point-handle-width) + var(--point-handle-border));--handle-color: #38e;--handle-background: #fff;--active-color: #1a70e5;--disabled-color: #999;--hover-color: rgba(56, 136, 238, .2)}svg text{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.handle{position:absolute;background-color:var(--handle-color)}.handle.line{cursor:move}.ogma-annotation-text-editor{display:flex;flex-direction:column;align-items:flex-end;position:relative;width:100%;height:100%}.ogma-annotation-text-editor.animate-width{transition:width .2s ease-out,left .2s ease-out}.ogma-annotation-text-editor textarea{outline:none;padding:0;margin:0;border:0;width:100%;height:100%;cursor:text;z-index:1000;resize:none;white-space:break-spaces;background-color:transparent;overflow:hidden}.ogma-annotation-text-editor textarea::-webkit-scrollbar{width:6px}.ogma-annotation-text-editor textarea::-webkit-scrollbar-track{background:transparent}.ogma-annotation-text-editor textarea::-webkit-scrollbar-thumb{background:#0003;border-radius:3px}.ogma-annotation-text-editor textarea::-webkit-scrollbar-thumb:hover{background:#0000004d}.ogma-send-button{width:24px;height:24px;margin-left:auto;border:none;background:transparent;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:background-color .2s ease;z-index:1001}.ogma-send-button-icon{width:16px;height:16px;display:flex;align-items:center;justify-content:center;color:var(--handle-color, #38e)}.ogma-send-button-icon svg{width:100%;height:100%}.ogma-send-button:not(:disabled):hover{background-color:var(--hover-color);transform:scale(1.05)}.ogma-send-button:not(:disabled):hover .ogma-send-button-icon{color:var(--active-color)}.ogma-send-button:not(:disabled):active{background-color:var(--hover-color);transform:scale(.95)}.ogma-send-button:not(:disabled):active .ogma-send-button-icon{color:var(--active-color)}.ogma-send-button:disabled{cursor:not-allowed;opacity:.3}.ogma-send-button:disabled .ogma-send-button-icon{color:var(--disabled-color)}.annotation-text-handle .noevents{pointer-events:none;z-index:20}.line-handle,.point-handle{z-index:30}.line-handle.top{cursor:ns-resize;height:var(--handle-width);left:0;width:100%;top:calc(var(--handle-width) * -1)}.line-handle.bottom{cursor:ns-resize;height:var(--handle-width);width:100%;left:0;bottom:calc(var(--handle-width) * -1)}.line-handle.left{cursor:ew-resize;height:100%;top:0;left:calc(var(--handle-width) * -1);width:var(--handle-width)}.line-handle.right{cursor:ew-resize;height:100%;right:calc(var(--handle-width) * -1);top:0;width:var(--handle-width)}.line-handle.top,.line-handle.bottom,.line-handle.left,.line-handle.right{cursor:move}.point-handle{border-radius:50%;background-color:#fff;transform-origin:center;transform:translate(-50%,-50%);border:var(--point-handle-border) solid var(--handle-color);width:var(--point-handle-width);height:var(--point-handle-width)}.point-handle.top-left{transform:translate(-55%,-55%)}.point-handle.top-right{transform:translate(-45%,-55%)}.point-handle.bottom-left{transform:translate(-55%,-45%)}.point-handle.bottom-right{transform:translate(-45%,-45%)}.rotated .point-handle.bottom-left,.rotated .point-handle.top-right{display:none}.point-handle.top-left,.point-handle.bottom-right{cursor:nwse-resize}.point-handle.top-right,.point-handle.bottom-left{cursor:nesw-resize}.point-handle.bottom-left,.point-handle.bottom-right{top:100%}.point-handle.top-left,.point-handle.top-right{top:0}.point-handle.bottom-left,.point-handle.top-left{left:0}.point-handle.bottom-right,.point-handle.top-right{left:100%}.arrow-handle>.handle{height:var(--point-handle-width);width:var(--point-handle-width);border-radius:50%;cursor:move;transform:translate(-50%,-50%)}.arrow-handle>.line{height:var(--handle-width);border-radius:0;cursor:move}.arrow-handle>.point{background-color:var(--handle-background);border:2px solid var(--handle-color)}.annotations-hidden-input{position:absolute;bottom:0;left:0;visibility:hidden;display:block}.active{background-color:#38e}.annotation-text{cursor:pointer}.annotation-text:hover>rect{stroke:var(--handle-color)}.annotation-text a{fill:var(--handle-color)}.annotation-text a:hover{text-decoration:underline}.annotation-hovered,.annotation-selected{background:#f5f5f5;background-color:#f5f5f5}
1
+ :root{--handle-width: 2px;--point-handle-width: calc(3 * var(--handle-width));--point-handle-border: 2px;--point-handle-total: calc(var(--point-handle-width) + var(--point-handle-border));--handle-color: #38e;--annotation-link-color: var(--handle-color);--handle-background: #fff;--active-color: #1a70e5;--disabled-color: #999;--hover-color: rgba(56, 136, 238, .2)}svg text{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ogma-annotation-link{fill:var(--annotation-link-color, #38e);color:var(--annotation-link-color, #38e);text-decoration:none;pointer-events:auto;cursor:pointer}.handle{position:absolute;background-color:var(--handle-color)}.handle.line{cursor:move}.ogma-annotation-text-editor{display:flex;flex-direction:column;align-items:flex-end;position:relative;width:100%;height:100%}.ogma-annotation-text-editor.animate-width{transition:width .2s ease-out,left .2s ease-out}.ogma-annotation-text-editor textarea{outline:none;padding:0;margin:0;border:0;width:100%;height:100%;cursor:text;z-index:1000;resize:none;white-space:break-spaces;background-color:transparent;overflow:hidden}.ogma-annotation-text-editor textarea::-webkit-scrollbar{width:6px}.ogma-annotation-text-editor textarea::-webkit-scrollbar-track{background:transparent}.ogma-annotation-text-editor textarea::-webkit-scrollbar-thumb{background:#0003;border-radius:3px}.ogma-annotation-text-editor textarea::-webkit-scrollbar-thumb:hover{background:#0000004d}.ogma-send-button{width:24px;height:24px;margin-left:auto;border:none;background:transparent;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:background-color .2s ease;z-index:1001}.ogma-send-button-icon{width:16px;height:16px;display:flex;align-items:center;justify-content:center;color:var(--handle-color, #38e)}.ogma-send-button-icon svg{width:100%;height:100%}.ogma-send-button:not(:disabled):hover{background-color:var(--hover-color);transform:scale(1.05)}.ogma-send-button:not(:disabled):hover .ogma-send-button-icon{color:var(--active-color)}.ogma-send-button:not(:disabled):active{background-color:var(--hover-color);transform:scale(.95)}.ogma-send-button:not(:disabled):active .ogma-send-button-icon{color:var(--active-color)}.ogma-send-button:disabled{cursor:not-allowed;opacity:.3}.ogma-send-button:disabled .ogma-send-button-icon{color:var(--disabled-color)}.annotation-text-handle .noevents{pointer-events:none;z-index:20}.line-handle,.point-handle{z-index:30}.line-handle.top{cursor:ns-resize;height:var(--handle-width);left:0;width:100%;top:calc(var(--handle-width) * -1)}.line-handle.bottom{cursor:ns-resize;height:var(--handle-width);width:100%;left:0;bottom:calc(var(--handle-width) * -1)}.line-handle.left{cursor:ew-resize;height:100%;top:0;left:calc(var(--handle-width) * -1);width:var(--handle-width)}.line-handle.right{cursor:ew-resize;height:100%;right:calc(var(--handle-width) * -1);top:0;width:var(--handle-width)}.line-handle.top,.line-handle.bottom,.line-handle.left,.line-handle.right{cursor:move}.point-handle{border-radius:50%;background-color:#fff;transform-origin:center;transform:translate(-50%,-50%);border:var(--point-handle-border) solid var(--handle-color);width:var(--point-handle-width);height:var(--point-handle-width)}.point-handle.top-left{transform:translate(-55%,-55%)}.point-handle.top-right{transform:translate(-45%,-55%)}.point-handle.bottom-left{transform:translate(-55%,-45%)}.point-handle.bottom-right{transform:translate(-45%,-45%)}.rotated .point-handle.bottom-left,.rotated .point-handle.top-right{display:none}.point-handle.top-left,.point-handle.bottom-right{cursor:nwse-resize}.point-handle.top-right,.point-handle.bottom-left{cursor:nesw-resize}.point-handle.bottom-left,.point-handle.bottom-right{top:100%}.point-handle.top-left,.point-handle.top-right{top:0}.point-handle.bottom-left,.point-handle.top-left{left:0}.point-handle.bottom-right,.point-handle.top-right{left:100%}.arrow-handle>.handle{height:var(--point-handle-width);width:var(--point-handle-width);border-radius:50%;cursor:move;transform:translate(-50%,-50%)}.arrow-handle>.line{height:var(--handle-width);border-radius:0;cursor:move}.arrow-handle>.point{background-color:var(--handle-background);border:2px solid var(--handle-color)}.annotations-hidden-input{position:absolute;bottom:0;left:0;visibility:hidden;display:block}.active{background-color:#38e}.annotation-text{cursor:pointer}.annotation-text:hover>rect{stroke:var(--handle-color)}.annotation-text a{fill:var(--handle-color)}.annotation-text a:hover{text-decoration:underline}.annotation-hovered,.annotation-selected{background:#f5f5f5;background-color:#f5f5f5}
@@ -128,6 +128,19 @@ export declare type Bounds = [number, number, number, number];
128
128
  export declare interface Box extends AnnotationFeature<Point_2, BoxProperties> {
129
129
  }
130
130
 
131
+ /**
132
+ * Arrow snapped to a rectangular annotation (text, box, comment).
133
+ * nx/ny are center-relative fractions multiplied by width/height:
134
+ * left-center = { nx: -0.5, ny: 0 }
135
+ * right-center = { nx: 0.5, ny: 0 }
136
+ * center = { nx: 0, ny: 0 }
137
+ */
138
+ export declare type BoxMagnet = {
139
+ type: "box";
140
+ nx: number;
141
+ ny: number;
142
+ };
143
+
131
144
  /** Properties specific to box annotations. */
132
145
  export declare interface BoxProperties extends AnnotationProps {
133
146
  type: "box";
@@ -543,6 +556,51 @@ export declare class Control extends default_2<FeatureEvents> {
543
556
  commentStyle?: Partial<CommentProps>;
544
557
  arrowStyle?: Partial<ArrowProperties>;
545
558
  }): this;
559
+ /**
560
+ * Enable sticky note drawing mode - drops a plain, resizable text box
561
+ * (empty content, "Quick note…" ghost placeholder, no connector arrow)
562
+ * like a Miro sticky note, unlike `enableCommentDrawing`. It's a regular
563
+ * `text` annotation, so it's placed the same interactive way as
564
+ * `enableBoxDrawing`/`enableTextDrawing`: click for a default-size square,
565
+ * or drag to size it - either way it keeps the usual corner/edge drag
566
+ * handles to resize it afterward.
567
+ *
568
+ * Call this method when the user clicks an "Add sticky note" button. The
569
+ * control will:
570
+ * 1. Wait for the next mousedown event
571
+ * 2. Create the note at that position and start the interactive
572
+ * corner-drag, already selected
573
+ * 3. On release: a plain click (no drag) gets a default square size, a
574
+ * drag gets sized to match instead - either way it drops straight
575
+ * into editing (the placeholder is just ghost text, so typing
576
+ * immediately replaces it)
577
+ * 4. Clean up automatically when done
578
+ *
579
+ * @example
580
+ * ```ts
581
+ * addStickyNoteButton.addEventListener('click', () => {
582
+ * control.enableStickyNoteDrawing({ background: '#FFEB99' });
583
+ * });
584
+ * ```
585
+ *
586
+ * @param style Sticky note style options (merged over the sticky note defaults)
587
+ * @returns this for chaining
588
+ * @see startStickyNote for low-level programmatic control
589
+ */
590
+ enableStickyNoteDrawing(style?: Partial<Text_2["properties"]["style"]>): this;
591
+ /**
592
+ * Enable erase mode: every click on an annotation deletes it immediately.
593
+ * Stays armed across multiple clicks until `disableEraseMode()` is called,
594
+ * or another drawing tool is enabled / `cancelDrawing()` is called.
595
+ *
596
+ * @returns this for chaining
597
+ * @see disableEraseMode to turn erase mode off
598
+ */
599
+ enableEraseMode(): this;
600
+ /** Turn erase mode off. No-op if it isn't active. */
601
+ disableEraseMode(): this;
602
+ /** Whether erase mode is currently active. */
603
+ isEraseModeActive(): boolean;
546
604
  /**
547
605
  * Place a pre-created annotation by moving it with the cursor.
548
606
  * The annotation follows the mouse until the user clicks to place it.
@@ -590,6 +648,21 @@ export declare class Control extends default_2<FeatureEvents> {
590
648
  commentStyle?: Partial<CommentProps>;
591
649
  arrowStyle?: Partial<ArrowProperties>;
592
650
  }): this;
651
+ /**
652
+ * **Advanced API:** Programmatically start drawing a sticky note at
653
+ * specific coordinates - same interactive corner-drag as `startBox`.
654
+ * You must handle mouse events yourself (or immediately release/complete
655
+ * it via the same events `enableStickyNoteDrawing` would).
656
+ *
657
+ * **For most use cases, use `enableStickyNoteDrawing()` instead.**
658
+ *
659
+ * @param x X coordinate for the note's top-left corner
660
+ * @param y Y coordinate for the note's top-left corner
661
+ * @param style Sticky note style options
662
+ * @returns this for chaining
663
+ * @see enableStickyNoteDrawing for the recommended high-level API
664
+ */
665
+ startStickyNote(x: number, y: number, style?: Partial<Text_2["properties"]["style"]>): this;
593
666
  /**
594
667
  * **Advanced API:** Programmatically start drawing a box at specific coordinates.
595
668
  *
@@ -1209,6 +1282,12 @@ declare interface DragEvent_2 {
1209
1282
  }
1210
1283
  export { DragEvent_2 as DragEvent }
1211
1284
 
1285
+ /** Arrow snapped at parametric position t (0–1) along an edge path. */
1286
+ export declare type EdgeMagnet = {
1287
+ type: "edge";
1288
+ t: number;
1289
+ };
1290
+
1212
1291
  export declare const EVT_ADD = "add";
1213
1292
 
1214
1293
  export declare const EVT_CANCEL_DRAWING = "cancelDrawing";
@@ -1239,6 +1318,11 @@ export declare const EVT_UNSELECT = "unselect";
1239
1318
 
1240
1319
  export declare const EVT_UPDATE = "update";
1241
1320
 
1321
+ /**
1322
+ * Serialized link stored inside arrow.properties.link.
1323
+ * Uses plain { x, y } for backward compatibility with saved annotations.
1324
+ * Converted to the internal Magnet type by Links.add().
1325
+ */
1242
1326
  export declare type ExportedLink = {
1243
1327
  id: Id;
1244
1328
  side: Side;
@@ -1540,13 +1624,12 @@ export declare interface Link {
1540
1624
  target: Id;
1541
1625
  /** Text or node */
1542
1626
  targetType: TargetType;
1543
- /**
1544
- * On which point relative to topleft corner the arrow is tighten, in case of
1545
- * node, a 0 vector represents the center, otherwise it can be deduced from the arrow itself
1546
- */
1547
- magnet: Point;
1627
+ /** Typed snap point — semantics depend on targetType, see Magnet union. */
1628
+ magnet: Magnet;
1548
1629
  }
1549
1630
 
1631
+ export declare type Magnet = NodeMagnet | EdgeMagnet | BoxMagnet | PolygonMagnet;
1632
+
1550
1633
  /**
1551
1634
  * Migrates old Polygon-based Box/Text to new Point-based format
1552
1635
  * Called only when annotations are added/loaded
@@ -1554,6 +1637,12 @@ export declare interface Link {
1554
1637
  */
1555
1638
  export declare function migrateBoxOrTextIfNeeded<T extends Annotation>(annotation: T): T;
1556
1639
 
1640
+ /** Arrow snapped to the center or perimeter of a node. */
1641
+ export declare type NodeMagnet = {
1642
+ type: "node";
1643
+ center: boolean;
1644
+ };
1645
+
1557
1646
  export declare const NONE = -1;
1558
1647
 
1559
1648
  export declare function parseColor(color: Color): {
@@ -1575,6 +1664,16 @@ export declare type Point = {
1575
1664
  export declare interface Polygon extends AnnotationFeature<Polygon_2, PolygonProperties> {
1576
1665
  }
1577
1666
 
1667
+ /**
1668
+ * Arrow snapped to a polygon annotation.
1669
+ * rx/ry are 0–1 fractions of the polygon's bounding box from its top-left corner.
1670
+ */
1671
+ export declare type PolygonMagnet = {
1672
+ type: "polygon";
1673
+ rx: number;
1674
+ ry: number;
1675
+ };
1676
+
1578
1677
  export declare interface PolygonProperties extends AnnotationProps {
1579
1678
  type: "polygon";
1580
1679
  style?: PolygonStyle;
@@ -1712,6 +1811,13 @@ export declare interface TextStyle extends BoxStyle {
1712
1811
  borderRadius?: number;
1713
1812
  /** When true, text maintains constant size regardless of zoom level */
1714
1813
  fixedSize?: boolean;
1814
+ /**
1815
+ * Ghost text shown (via the textarea's native `placeholder` attribute)
1816
+ * while `content` is empty - disappears the instant the user types, no
1817
+ * selection/focus tricks needed. Overrides the global
1818
+ * `ControllerOptions.textPlaceholder` for this annotation.
1819
+ */
1820
+ placeholder?: string;
1715
1821
  }
1716
1822
 
1717
1823
  /** @private */