@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.
package/dist/ui.mjs ADDED
@@ -0,0 +1,533 @@
1
+ var x = Object.defineProperty;
2
+ var $ = (r, t, e) => t in r ? x(r, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : r[t] = e;
3
+ var l = (r, t, e) => $(r, typeof t != "symbol" ? t + "" : t, e);
4
+ import { RgbaBase as M } from "vanilla-colorful/lib/entrypoints/rgba";
5
+ import { j as g, s as L, t as T, i as P, v as k, h as b, G as d, aH as B, D as C, ah as E, ac as S } from "./Polygon-DVFVmDbR.js";
6
+ const y = "rgba-color-picker";
7
+ class D extends M {
8
+ }
9
+ function F() {
10
+ return customElements.get(y) || customElements.define(y, D), document.createElement(y);
11
+ }
12
+ const O = [
13
+ { value: "#f5f5f5", style: "--circle-color: #f5f5f5;" },
14
+ { value: "#EDE6FF", style: "--circle-color: #EDE6FF;" },
15
+ {
16
+ value: "transparent",
17
+ style: "--circle-color: white; border: 2px dashed #ccc;"
18
+ }
19
+ ], m = [
20
+ { value: "sans-serif", label: "Sans Serif", icon: "type" },
21
+ { value: "serif", label: "Serif", icon: "italic" },
22
+ { value: "monospace", label: "Monospace", icon: "code" }
23
+ ], N = [
24
+ { value: "none", label: "None", icon: "x" },
25
+ { value: "arrow", label: "Open Arrow", icon: "arrow-left" },
26
+ { value: "arrow-plain", label: "Filled Arrow", icon: "play" },
27
+ { value: "halo-dot", label: "Halo Dot", icon: "circle-dot" },
28
+ { value: "dot", label: "Dot", icon: "dot" }
29
+ ], R = [
30
+ { value: "plain", icon: "circle" },
31
+ { value: "dashed", icon: "circle-dashed" }
32
+ ], H = ["#0099FF", "#FF7523", "#44AA99"];
33
+ function I(r) {
34
+ return `rgba(${r.r}, ${r.g}, ${r.b}, ${r.a})`;
35
+ }
36
+ const _ = 3;
37
+ function z() {
38
+ return { colors: [...H], activeIndex: 0 };
39
+ }
40
+ function q(r, t) {
41
+ const e = r.colors.indexOf(t);
42
+ return e === -1 ? { colors: [t, ...r.colors].slice(0, _), activeIndex: 0 } : { colors: r.colors, activeIndex: e };
43
+ }
44
+ const W = {
45
+ "chevron-down": '<path d="m6 9 6 6 6-6"/>',
46
+ x: '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
47
+ "arrow-left": '<path d="m12 19-7-7 7-7"/><path d="M19 12H5"/>',
48
+ "arrow-right": '<path d="M5 12h14"/><path d="m12 5 7 7-7 7"/>',
49
+ play: '<polygon points="6 3 20 12 6 21 6 3"/>',
50
+ "circle-dot": '<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="1"/>',
51
+ dot: '<circle cx="12.1" cy="12.1" r="1"/>',
52
+ circle: '<circle cx="12" cy="12" r="10"/>',
53
+ "circle-dashed": '<path d="M10.1 2.182a10 10 0 0 1 3.8 0"/><path d="M13.9 21.818a10 10 0 0 1-3.8 0"/><path d="M17.609 3.721a10 10 0 0 1 2.69 2.7"/><path d="M2.182 13.9a10 10 0 0 1 0-3.8"/><path d="M20.279 17.609a10 10 0 0 1-2.7 2.69"/><path d="M21.818 10.1a10 10 0 0 1 0 3.8"/><path d="M3.721 6.391a10 10 0 0 1 2.7-2.69"/><path d="M6.391 20.279a10 10 0 0 1-2.69-2.7"/>',
54
+ type: '<path d="M4 7V4h16v3"/><path d="M9 20h6"/><path d="M12 4v16"/>',
55
+ italic: '<line x1="19" x2="10" y1="4" y2="4"/><line x1="14" x2="5" y1="20" y2="20"/><line x1="15" x2="9" y1="4" y2="20"/>',
56
+ code: '<polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/>',
57
+ trash: '<path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/>',
58
+ undo: '<path d="M3 7v6h6"/><path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"/>',
59
+ redo: '<path d="M21 7v6h-6"/><path d="M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3l3 2.7"/>',
60
+ pentagon: '<path d="M10.83 2.38a2 2 0 0 1 2.34 0l8 5.74a2 2 0 0 1 .73 2.25l-3.04 9.26a2 2 0 0 1-1.9 1.37H7.04a2 2 0 0 1-1.9-1.37L2.1 10.37a2 2 0 0 1 .73-2.25z"/>',
61
+ "rectangle-horizontal": '<rect width="20" height="12" x="2" y="6" rx="2"/>',
62
+ "message-square": '<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>',
63
+ "sticky-note": '<path d="M21 9a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 15 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2z"/><path d="M15 3v5a1 1 0 0 0 1 1h5"/>',
64
+ eraser: '<path d="M21 21H8a2 2 0 0 1-1.42-.587l-3.994-3.999a2 2 0 0 1 0-2.828l10-10a2 2 0 0 1 2.829 0l5.999 6a2 2 0 0 1 0 2.828L12.834 21"/><path d="m5.082 11.09 8.828 8.828"/>',
65
+ download: '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/>',
66
+ camera: '<path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/><circle cx="12" cy="13" r="3"/>',
67
+ "rotate-cw": '<path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/>',
68
+ "rotate-ccw": '<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/>',
69
+ minimize: '<path d="M8 3v3a2 2 0 0 1-2 2H3"/><path d="M21 8h-3a2 2 0 0 1-2-2V3"/><path d="M3 16h3a2 2 0 0 1 2 2v3"/><path d="M16 21v-3a2 2 0 0 1 2-2h3"/>'
70
+ };
71
+ function v(r, t = 18) {
72
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${t}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${W[r]}</svg>`;
73
+ }
74
+ const V = 150;
75
+ function U(r, { onShow: t, onHide: e }) {
76
+ let o = null, i = null;
77
+ const n = () => {
78
+ i !== null && (clearTimeout(i), i = null);
79
+ }, s = () => {
80
+ n(), o && (t(o), o = null);
81
+ }, a = (h) => {
82
+ if (n(), h.ids.length === 1) {
83
+ const u = r.getAnnotation(h.ids[0]);
84
+ if (!u) return;
85
+ if (o = u, r.isDrawing()) {
86
+ r.once("cancelDrawing", s), r.once("completeDrawing", s);
87
+ return;
88
+ }
89
+ i = setTimeout(s, V);
90
+ } else
91
+ o = null, e();
92
+ }, c = () => {
93
+ n(), o = null, e();
94
+ }, p = () => {
95
+ n(), o = null, e();
96
+ };
97
+ return r.on("select", a), r.on("click", s), r.on("dragend", s), r.on("dragstart", c), r.on("unselect", p), () => {
98
+ n(), r.off("select", a), r.off("click", s), r.off("dragend", s), r.off("dragstart", c), r.off("unselect", p), r.off("cancelDrawing", s), r.off("completeDrawing", s);
99
+ };
100
+ }
101
+ const G = "right", Y = "vertical";
102
+ class Z {
103
+ constructor(t) {
104
+ l(this, "control");
105
+ l(this, "panel");
106
+ l(this, "panelBody");
107
+ l(this, "mode", null);
108
+ l(this, "currentAnnotation", null);
109
+ l(this, "currentColor", "#0099FF");
110
+ l(this, "recent", z());
111
+ l(this, "colorCircles", []);
112
+ l(this, "colorPickerOverlay", null);
113
+ l(this, "colorPicker", null);
114
+ l(this, "detachVisibility");
115
+ l(this, "documentClickHandler");
116
+ l(this, "hide", () => {
117
+ this.panel.style.display = "none", this.closeColorPicker(), this.currentAnnotation = null, this.mode = null;
118
+ });
119
+ this.control = t.control;
120
+ const e = t.container ?? document.body;
121
+ this.panel = document.createElement("div"), this.panel.className = "annotation-panel", this.panel.style.display = "none", this.panel.dataset.placement = t.placement ?? G, this.panel.dataset.orientation = t.orientation ?? Y, this.panelBody = document.createElement("div"), this.panelBody.className = "panel-body", this.panel.appendChild(this.panelBody), e.appendChild(this.panel), this.detachVisibility = U(this.control, {
122
+ onShow: (o) => {
123
+ this.setAnnotation(o), this.show();
124
+ },
125
+ onHide: this.hide
126
+ }), ["click", "mousedown", "mousemove"].forEach(
127
+ (o) => this.panel.addEventListener(o, (i) => i.stopPropagation())
128
+ ), this.documentClickHandler = (o) => {
129
+ this.colorPickerOverlay && !this.colorPickerOverlay.contains(o.target) && !this.colorCircles.some((i) => i.contains(o.target)) && this.closeColorPicker(), this.panelBody.querySelectorAll(".custom-select").forEach((i) => i.classList.remove("open"));
130
+ }, document.addEventListener("click", this.documentClickHandler);
131
+ }
132
+ setAnnotation(t) {
133
+ this.currentAnnotation = t, g(t) ? (this.mode = "arrow", this.renderArrow(t)) : L(t) || T(t) || P(t) ? (this.mode = "text", this.renderText(t)) : k(t) && (this.mode = "polygon", this.renderPolygon(t));
134
+ }
135
+ renderArrow(t) {
136
+ const e = t.properties.style || {};
137
+ this.updateColorFromAnnotation(
138
+ e.strokeColor || b.strokeColor
139
+ ), this.panelBody.innerHTML = `
140
+ ${this.section("Color", this.colorSelector())}
141
+ ${this.section(
142
+ "Extremities",
143
+ `<div class="custom-select-section">
144
+ ${this.extremitySelector("head", t)}
145
+ ${this.extremitySelector("tail", t)}
146
+ </div>`
147
+ )}
148
+ ${this.slider("Stroke width", "line-width", e.strokeWidth || b.strokeWidth, 1, 20)}
149
+ ${this.lineTypeButtons(e.strokeType || "plain")}
150
+ `, this.bind();
151
+ }
152
+ renderText(t) {
153
+ const e = t.properties.style || {};
154
+ this.updateColorFromAnnotation(e.color || d.color);
155
+ const o = typeof e.fontSize == "number" ? e.fontSize : typeof d.fontSize == "number" ? d.fontSize : 18;
156
+ this.panelBody.innerHTML = `
157
+ ${this.section("Color", this.colorSelector())}
158
+ ${this.section("Background", this.backgroundSelector(e.background || d.background))}
159
+ ${this.section("Font", `<div class="custom-select-section">${this.fontSelector(e.font || d.font)}</div>`)}
160
+ ${this.slider("Font size", "font-size", o, 8, 72)}
161
+ ${this.slider("Stroke width", "line-width", e.strokeWidth || d.strokeWidth, 1, 20)}
162
+ ${this.lineTypeButtons(e.strokeType || "plain")}
163
+ `, this.bind();
164
+ }
165
+ renderPolygon(t) {
166
+ const e = t.properties.style || {};
167
+ this.updateColorFromAnnotation(e.strokeColor || "#000000"), this.panelBody.innerHTML = `
168
+ ${this.section("Color", this.colorSelector())}
169
+ ${this.section("Fill", this.backgroundSelector(e.background || "transparent"))}
170
+ ${this.slider("Stroke width", "line-width", e.strokeWidth || 2, 1, 20)}
171
+ ${this.lineTypeButtons(e.strokeType || "plain")}
172
+ `, this.bind();
173
+ }
174
+ // Rendering helpers
175
+ section(t, e) {
176
+ return `<div class="section-header"><h3>${t}</h3></div>${e}`;
177
+ }
178
+ icon(t, e = !1) {
179
+ return e ? `<span style="display:inline-flex;transform:rotate(180deg)">${v(t)}</span>` : v(t);
180
+ }
181
+ colorSelector() {
182
+ return `<div class="color-selector">${this.recent.colors.map(
183
+ (t, e) => `
184
+ <button class="color-circle ${e === 0 ? "color-circle-primary" : ""}" data-index="${e}" data-color="${t}">
185
+ <div class="color-inner"></div>
186
+ </button>
187
+ `
188
+ ).join("")}</div>`;
189
+ }
190
+ backgroundSelector(t) {
191
+ return `<div class="color-selector">${O.map(
192
+ ({ value: e, style: o }) => `
193
+ <button class="color-circle ${e === t ? "color-circle-primary" : ""}" data-background-color="${e}">
194
+ <div class="color-inner" style="${o}"></div>
195
+ </button>
196
+ `
197
+ ).join("")}</div>`;
198
+ }
199
+ fontSelector(t) {
200
+ const e = m.find((o) => o.value === t) || m[0];
201
+ return this.dropdown(
202
+ "font",
203
+ e,
204
+ m.map((o) => ({ ...o, selected: o.value === t }))
205
+ );
206
+ }
207
+ extremitySelector(t, e) {
208
+ var s;
209
+ const o = ((s = e.properties.style) == null ? void 0 : s[t]) || "none", i = N.map((a) => ({
210
+ ...a,
211
+ icon: a.value === "arrow" && t === "tail" ? "arrow-left" : a.value === "arrow" ? "arrow-right" : a.icon,
212
+ selected: a.value === o,
213
+ rotate: a.value === "arrow-plain" && t === "tail"
214
+ })), n = i.find((a) => a.selected) || i[0];
215
+ return `<div class="extremity-wrapper"><label>${t}</label>${this.dropdown(t, n, i, `data-end="${t}"`)}</div>`;
216
+ }
217
+ dropdown(t, e, o, i = "") {
218
+ return `<div class="custom-select" data-type="${t}" ${i}>
219
+ <div class="custom-select-trigger">
220
+ ${this.icon(e.icon, e.rotate)}
221
+ <span>${e.label}</span>
222
+ ${this.icon("chevron-down")}
223
+ </div>
224
+ <div class="custom-select-options">${o.map(
225
+ (n) => `
226
+ <div class="custom-select-option ${n.selected ? "selected" : ""}" data-value="${n.value}" data-icon="${n.icon}" data-rotate="${n.rotate ? "1" : ""}" title="${n.label}">
227
+ ${this.icon(n.icon, n.rotate)}
228
+ <span>${n.label}</span>
229
+ </div>
230
+ `
231
+ ).join("")}</div>
232
+ </div>`;
233
+ }
234
+ slider(t, e, o, i, n) {
235
+ return `${this.section(
236
+ t,
237
+ `<div class="slider-section">
238
+ <input type="range" id="${e}-slider" class="slider" min="${i}" max="${n}" value="${o}">
239
+ <div class="slider-value"><span id="${e}-value">${o}</span></div>
240
+ </div>`
241
+ )}`;
242
+ }
243
+ lineTypeButtons(t) {
244
+ return `${this.section(
245
+ "Line type",
246
+ `<div class="linetype-section">${R.map(
247
+ ({ value: e, icon: o }) => `
248
+ <button class="linetype-button ${t === e ? "active" : ""}" data-linetype="${e}" title="${e}">
249
+ ${this.icon(o)}
250
+ </button>
251
+ `
252
+ ).join("")}</div>`
253
+ )}`;
254
+ }
255
+ // Event binding - unified for all modes
256
+ bind() {
257
+ this.colorCircles = Array.from(
258
+ this.panelBody.querySelectorAll("[data-index]")
259
+ ), this.colorCircles.forEach((t, e) => {
260
+ t.addEventListener("click", (o) => {
261
+ o.stopPropagation();
262
+ const i = this.recent.activeIndex === e && t.classList.contains("color-circle-primary");
263
+ this.recent = { ...this.recent, activeIndex: e }, this.currentColor = this.recent.colors[e], this.updateColorCircles(), i ? this.toggleColorPicker(t) : this.mode === "arrow" ? this.updateStyle({ strokeColor: this.currentColor }) : this.mode === "text" && this.updateStyle({ strokeColor: this.currentColor });
264
+ });
265
+ }), this.updateColorCircles(), this.panelBody.querySelectorAll("[data-background-color]").forEach((t) => {
266
+ t.addEventListener(
267
+ "click",
268
+ () => this.updateStyle({ background: t.dataset.backgroundColor })
269
+ );
270
+ }), this.panelBody.querySelectorAll(".custom-select").forEach((t) => {
271
+ const e = t.querySelector(
272
+ ".custom-select-trigger"
273
+ ), o = t.querySelectorAll(
274
+ ".custom-select-option"
275
+ );
276
+ e.addEventListener("click", (i) => {
277
+ i.stopPropagation(), this.panelBody.querySelectorAll(".custom-select").forEach((n) => {
278
+ n !== t && n.classList.remove("open");
279
+ }), t.classList.toggle("open");
280
+ }), o.forEach((i) => {
281
+ i.addEventListener("click", (n) => {
282
+ n.stopPropagation();
283
+ const s = i.dataset.value;
284
+ o.forEach((w) => w.classList.remove("selected")), i.classList.add("selected");
285
+ const a = i.dataset.icon, c = i.dataset.rotate === "1", p = i.querySelector("span").textContent || "", h = e.querySelector("svg, span[style]");
286
+ h && (h.outerHTML = this.icon(a, c)), e.querySelector("span").textContent = p, t.classList.remove("open");
287
+ const u = t.dataset.type, f = t.dataset.end;
288
+ u === "font" ? this.updateStyle({ font: s }) : f === "head" ? this.updateStyle({ head: s }) : f === "tail" && this.updateStyle({ tail: s });
289
+ });
290
+ });
291
+ }), ["line-width", "font-size"].forEach((t) => {
292
+ const e = this.panelBody.querySelector(
293
+ `#${t}-slider`
294
+ ), o = this.panelBody.querySelector(`#${t}-value`);
295
+ e && o && e.addEventListener("input", () => {
296
+ const i = parseInt(e.value, 10);
297
+ o.textContent = i.toString(), t === "line-width" ? this.updateStyle(
298
+ this.mode === "text" ? { strokeWidth: i, strokeColor: this.currentColor } : { strokeWidth: i }
299
+ ) : t === "font-size" && this.updateStyle({ fontSize: i });
300
+ });
301
+ }), this.panelBody.querySelectorAll(".linetype-button").forEach((t) => {
302
+ t.addEventListener("click", () => {
303
+ const e = t.dataset.linetype;
304
+ this.panelBody.querySelectorAll(".linetype-button").forEach((o) => o.classList.remove("active")), t.classList.add("active"), this.updateStyle({ strokeType: e });
305
+ });
306
+ });
307
+ }
308
+ // Color management
309
+ updateColorFromAnnotation(t) {
310
+ this.recent = q(this.recent, t), this.currentColor = t;
311
+ }
312
+ updateColorCircles() {
313
+ this.colorCircles.forEach((t, e) => {
314
+ t.setAttribute("data-color", this.recent.colors[e]), t.style.setProperty("--circle-color", this.recent.colors[e]), t.classList.toggle(
315
+ "color-circle-primary",
316
+ e === this.recent.activeIndex
317
+ );
318
+ });
319
+ }
320
+ toggleColorPicker(t) {
321
+ if (this.colorPickerOverlay) {
322
+ this.closeColorPicker();
323
+ return;
324
+ }
325
+ this.colorPickerOverlay = document.createElement("div"), this.colorPickerOverlay.className = "color-picker-overlay", document.body.appendChild(this.colorPickerOverlay), this.colorPicker = F(), this.colorPicker.color = B(this.currentColor), this.colorPickerOverlay.appendChild(this.colorPicker);
326
+ const e = t.getBoundingClientRect();
327
+ this.colorPickerOverlay.style.right = `${window.innerWidth - e.right}px`, this.colorPickerOverlay.style.top = `${e.bottom + 10}px`, this.colorPicker.addEventListener("color-changed", (o) => {
328
+ this.currentColor = I(o.detail.value), this.recent.colors[this.recent.activeIndex] = this.currentColor, this.updateColorCircles(), this.mode === "arrow" ? this.updateStyle({ strokeColor: this.currentColor }) : this.mode === "text" ? this.updateStyle({ color: this.currentColor }) : this.mode === "polygon" && this.updateStyle({ strokeColor: this.currentColor });
329
+ });
330
+ }
331
+ closeColorPicker() {
332
+ this.colorPickerOverlay && (this.colorPickerOverlay.remove(), this.colorPickerOverlay = null, this.colorPicker = null);
333
+ }
334
+ // Unified update method
335
+ updateStyle(t) {
336
+ this.currentAnnotation && (g(this.currentAnnotation) ? this.control.updateStyle(this.currentAnnotation.id, t) : k(this.currentAnnotation) ? this.control.updateStyle(this.currentAnnotation.id, t) : this.control.updateStyle(this.currentAnnotation.id, t));
337
+ }
338
+ // Layout
339
+ setPlacement(t) {
340
+ this.panel.dataset.placement = t;
341
+ }
342
+ setOrientation(t) {
343
+ this.panel.dataset.orientation = t;
344
+ }
345
+ // Visibility
346
+ show() {
347
+ this.panel.style.display = "block";
348
+ }
349
+ destroy() {
350
+ this.closeColorPicker(), this.detachVisibility(), document.removeEventListener("click", this.documentClickHandler), this.panel.remove();
351
+ }
352
+ }
353
+ const j = "bottom", X = "horizontal", A = [
354
+ "arrow",
355
+ "comment",
356
+ "sticky-note",
357
+ "box",
358
+ "text",
359
+ "polygon"
360
+ ];
361
+ class tt {
362
+ constructor(t) {
363
+ l(this, "control");
364
+ l(this, "root");
365
+ l(this, "activeButton", null);
366
+ l(this, "undoButton");
367
+ l(this, "redoButton");
368
+ l(this, "handleDrawingEnd", () => this.setActiveMode(null));
369
+ l(this, "handleHistory", () => this.updateUndoRedo());
370
+ this.control = t.control;
371
+ const e = t.container ?? document.body;
372
+ this.root = document.createElement("div"), this.root.className = "annotation-toolbar", this.root.dataset.placement = t.placement ?? j, this.root.dataset.orientation = t.orientation ?? X, e.appendChild(this.root), ["click", "mousedown", "mousemove"].forEach(
373
+ (s) => this.root.addEventListener(s, (a) => a.stopPropagation())
374
+ );
375
+ const o = t.enabledTypes ?? A, i = t.styles ?? {};
376
+ A.filter((s) => o.includes(s)).forEach(
377
+ (s) => this.root.appendChild(this.drawingButton(s, i))
378
+ ), o.length > 0 && this.root.appendChild(this.separator()), this.undoButton = this.button(null, "undo", "Undo", () => this.control.undo()), this.redoButton = this.button(null, "redo", "Redo", () => this.control.redo()), this.root.appendChild(this.undoButton), this.root.appendChild(this.redoButton);
379
+ const n = t.deleteMode ?? "erase";
380
+ (n === "erase" || n === "both") && (this.root.appendChild(this.separator()), this.root.appendChild(
381
+ this.button(
382
+ "erase",
383
+ "eraser",
384
+ "Erase (click annotations to delete them)",
385
+ () => {
386
+ this.control.isEraseModeActive() ? (this.control.disableEraseMode(), this.setActiveMode(null)) : (this.control.enableEraseMode(), this.setActiveMode("erase"));
387
+ }
388
+ )
389
+ )), (n === "select" || n === "both") && (n === "select" && this.root.appendChild(this.separator()), this.root.appendChild(
390
+ this.button(null, "trash", "Delete selected", () => {
391
+ const s = this.control.getSelectedAnnotations();
392
+ s.features.length > 0 && this.control.remove(s);
393
+ })
394
+ )), (t.onJsonExport || t.onSvgExport) && (this.root.appendChild(this.separator()), t.onJsonExport && this.root.appendChild(
395
+ this.button(null, "download", "Export annotations", t.onJsonExport)
396
+ ), t.onSvgExport && this.root.appendChild(
397
+ this.button(null, "camera", "Export SVG", t.onSvgExport)
398
+ )), this.control.on(C, this.handleDrawingEnd), this.control.on(E, this.handleDrawingEnd), this.control.on(S, this.handleHistory), this.updateUndoRedo();
399
+ }
400
+ drawingButton(t, e) {
401
+ switch (t) {
402
+ case "arrow":
403
+ return this.button("arrow", "arrow-right", "Add arrow", () => {
404
+ this.control.enableArrowDrawing({
405
+ strokeType: "plain",
406
+ strokeColor: "#3A03CF",
407
+ strokeWidth: 2,
408
+ head: "arrow",
409
+ ...e.arrow
410
+ }), this.setActiveMode("arrow");
411
+ });
412
+ case "comment":
413
+ return this.button("comment", "message-square", "Add comment", () => {
414
+ var n, s, a, c;
415
+ const o = (n = e.comment) == null ? void 0 : n.commentStyle, i = (s = e.comment) == null ? void 0 : s.arrowStyle;
416
+ this.control.enableCommentDrawing({
417
+ offsetX: ((a = e.comment) == null ? void 0 : a.offsetX) ?? 200,
418
+ offsetY: ((c = e.comment) == null ? void 0 : c.offsetY) ?? -150,
419
+ commentStyle: {
420
+ content: "",
421
+ ...o,
422
+ style: {
423
+ color: "#3A03CF",
424
+ background: "#EDE6FF",
425
+ fontSize: 16,
426
+ font: "IBM Plex Sans",
427
+ ...o == null ? void 0 : o.style
428
+ }
429
+ },
430
+ arrowStyle: {
431
+ ...i,
432
+ style: {
433
+ strokeType: "plain",
434
+ strokeColor: "#3A03CF",
435
+ strokeWidth: 2,
436
+ head: "halo-dot",
437
+ ...i == null ? void 0 : i.style
438
+ }
439
+ }
440
+ }), this.setActiveMode("comment");
441
+ });
442
+ case "sticky-note":
443
+ return this.button(
444
+ "sticky-note",
445
+ "sticky-note",
446
+ "Add sticky note",
447
+ () => {
448
+ this.control.enableStickyNoteDrawing({
449
+ ...e.stickyNote
450
+ }), this.setActiveMode("sticky-note");
451
+ }
452
+ );
453
+ case "box":
454
+ return this.button("box", "rectangle-horizontal", "Add box", () => {
455
+ this.control.enableBoxDrawing({
456
+ background: "#EDE6FF",
457
+ borderRadius: 8,
458
+ padding: 12,
459
+ ...e.box
460
+ }), this.setActiveMode("box");
461
+ });
462
+ case "text":
463
+ return this.button("text", "type", "Add text", () => {
464
+ this.control.enableTextDrawing({
465
+ font: "IBM Plex Sans",
466
+ fontSize: 24,
467
+ color: "#3A03CF",
468
+ background: "#EDE6FF",
469
+ borderRadius: 8,
470
+ padding: 12,
471
+ ...e.text
472
+ }), this.setActiveMode("text");
473
+ });
474
+ case "polygon":
475
+ return this.button(
476
+ "polygon",
477
+ "pentagon",
478
+ "Add polygon (click points, Esc to finish)",
479
+ () => {
480
+ this.control.enablePolygonDrawing({
481
+ strokeColor: "#3A03CF",
482
+ strokeWidth: 2,
483
+ background: "rgba(58, 3, 207, 0.15)",
484
+ ...e.polygon
485
+ }), this.setActiveMode("polygon");
486
+ }
487
+ );
488
+ }
489
+ }
490
+ button(t, e, o, i) {
491
+ const n = document.createElement("button");
492
+ return n.dataset.tooltip = o, n.innerHTML = v(e, 16), n.addEventListener("click", i), t && (n.dataset.mode = t), n;
493
+ }
494
+ separator() {
495
+ const t = document.createElement("span");
496
+ return t.className = "separator", t;
497
+ }
498
+ setActiveMode(t) {
499
+ this.activeButton && this.activeButton.classList.remove("active"), this.activeButton = t ? this.root.querySelector(`[data-mode="${t}"]`) : null, this.activeButton && this.activeButton.classList.add("active");
500
+ }
501
+ updateUndoRedo() {
502
+ this.undoButton.disabled = !this.control.canUndo(), this.redoButton.disabled = !this.control.canRedo();
503
+ }
504
+ // Layout
505
+ setPlacement(t) {
506
+ this.root.dataset.placement = t;
507
+ }
508
+ setOrientation(t) {
509
+ this.root.dataset.orientation = t;
510
+ }
511
+ destroy() {
512
+ this.control.off(C, this.handleDrawingEnd), this.control.off(E, this.handleDrawingEnd), this.control.off(S, this.handleHistory), this.root.remove();
513
+ }
514
+ }
515
+ export {
516
+ Z as AnnotationPanel,
517
+ tt as AnnotationToolbar,
518
+ O as BACKGROUNDS,
519
+ Y as DEFAULT_PANEL_ORIENTATION,
520
+ G as DEFAULT_PANEL_PLACEMENT,
521
+ H as DEFAULT_RECENT_COLORS,
522
+ N as EXTREMITY_OPTIONS,
523
+ m as FONTS,
524
+ W as ICON_PATHS,
525
+ R as LINE_TYPES,
526
+ _ as MAX_RECENT_COLORS,
527
+ U as attachPanelVisibility,
528
+ F as createRgbaColorPicker,
529
+ z as initialRecentColors,
530
+ I as rgbaToString,
531
+ v as svgIcon,
532
+ q as withColorFromAnnotation
533
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linkurious/ogma-annotations",
3
- "version": "2.0.3",
3
+ "version": "2.1.1",
4
4
  "description": "Headless annotation plugin for Ogma",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -12,7 +12,8 @@
12
12
  "dist/*.mjs",
13
13
  "dist/*.js",
14
14
  "dist/**/*.css",
15
- "dist/types"
15
+ "dist/types",
16
+ "src/style.d.ts"
16
17
  ],
17
18
  "exports": {
18
19
  ".": {
@@ -21,17 +22,34 @@
21
22
  "require": "./dist/index.js",
22
23
  "default": "./dist/index.mjs"
23
24
  },
25
+ "./ui": {
26
+ "types": "./dist/types/ui.d.ts",
27
+ "import": "./dist/ui.mjs",
28
+ "require": "./dist/ui.js",
29
+ "default": "./dist/ui.mjs"
30
+ },
31
+ "./ui/styles.css": {
32
+ "style": {
33
+ "default": "./dist/ui-styles.css"
34
+ },
35
+ "import": {
36
+ "default": "./dist/ui-styles.css"
37
+ },
38
+ "require": {
39
+ "default": "./dist/ui-styles.css"
40
+ }
41
+ },
24
42
  "./style.css": {
25
43
  "style": {
26
- "default": "./src/style.css"
44
+ "default": "./dist/ogma-annotations.css"
27
45
  },
28
46
  "import": {
29
47
  "types": "./src/style.d.ts",
30
- "default": "./src/style.css"
48
+ "default": "./dist/ogma-annotations.css"
31
49
  },
32
50
  "require": {
33
51
  "types": "./src/style.d.ts",
34
- "default": "./src/style.css"
52
+ "default": "./dist/ogma-annotations.css"
35
53
  }
36
54
  },
37
55
  "./umd": {
@@ -51,6 +69,7 @@
51
69
  "bump:minor": "bump2version minor && npm version --no-git-tag-version minor",
52
70
  "bump:major": "bump2version major && npm version --no-git-tag-version major",
53
71
  "build": "vite build",
72
+ "postbuild": "cp src/ui/styles.css dist/ui-styles.css",
54
73
  "lint": "npx oxlint -c oxlint.json src",
55
74
  "lint:ci": "mkdir -p reports && npx oxlint -c oxlint.json -f checkstyle src > reports/checkstyle.xml",
56
75
  "types": "tsc --d -emitDeclarationOnly --outDir dist/types",
@@ -88,29 +107,29 @@
88
107
  "@linkurious/ogma": ">=5.3.11"
89
108
  },
90
109
  "dependencies": {
91
- "@borgar/textbox": "1.8.0",
110
+ "@chenglou/pretext": "0.0.7",
92
111
  "eventemitter3": "5.0.1",
93
112
  "geojson": "0.5.0",
94
113
  "rbush": "4.0.1",
114
+ "vanilla-colorful": "0.7.2",
95
115
  "zundo": "2.3.0",
96
116
  "zustand": "5.0.7"
97
117
  },
98
118
  "devDependencies": {
99
119
  "@linkurious/eslint-config-ogma": "2.1.0",
100
120
  "oxlint": "0.16.0",
101
- "@types/geojson": "7946.0.13",
121
+ "@types/geojson": "7946.0.16",
102
122
  "@types/rbush": "4.0.0",
103
123
  "get-port": "7.0.0",
104
124
  "jsdom": "24.0.0",
105
125
  "playwright": "1.57.0",
106
- "typedoc": "0.28.15",
126
+ "typedoc": "0.28.20",
107
127
  "typedoc-plugin-markdown": "4.9.0",
108
128
  "typedoc-vitepress-theme": "1.1.2",
109
129
  "typescript": "5.3.3",
110
- "vanilla-colorful": "0.7.2",
111
- "vite": "6.3.5",
130
+ "vite": "6.4.3",
112
131
  "vite-plugin-dts": "^4.1.0",
113
- "vitest": "3.2.4",
132
+ "vitest": "3.2.6",
114
133
  "vitest-canvas-mock": "1.1.3"
115
134
  },
116
135
  "prettier": {
package/src/style.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ declare const css: string;
2
+ export default css;