@threadlabs/looma 0.1.0 → 0.1.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/README.md CHANGED
@@ -15,7 +15,7 @@ import { EditorToolbar, getDefaultEditorExtensions } from "@threadlabs/looma/vue
15
15
 
16
16
  Vue and Tiptap are optional to the package as a whole. `/vue` needs only Vue 3.5
17
17
  or newer. Looma's `/editor` and `/vue/editor` surfaces are Tiptap-backed and
18
- require the declared Tiptap 2 peers; `/vue/editor` consumers must also install
19
- `@tiptap/vue-3@^2.11.5` for `useEditor` and `EditorContent`. `/editor/ui` is the
20
- low-level UI-only boundary. See the repository getting-started guide for the
21
- full CSS and adapter setup.
18
+ ship Looma's concrete extension set within those subpaths; `/vue/editor` consumers
19
+ must also install `@tiptap/vue-3@^2.11.5` for `useEditor`, `EditorContent`, and a
20
+ compatible Tiptap core. `/editor/ui` is the low-level UI-only boundary. See the
21
+ repository getting-started guide for the full CSS and adapter setup.
@@ -11,6 +11,7 @@ function dispatchTableAction(element, action) {
11
11
  }
12
12
  if (typeof HTMLElement !== "undefined") {
13
13
  class UIEditorTableContextMenuElement extends HTMLElement {
14
+ positionFrame = null;
14
15
  static get observedAttributes() {
15
16
  return [
16
17
  "open",
@@ -32,9 +33,15 @@ if (typeof HTMLElement !== "undefined") {
32
33
  connectedCallback() {
33
34
  this.render();
34
35
  this.addEventListener("click", this.onClick);
36
+ window.addEventListener("resize", this.scheduleViewportPosition);
35
37
  }
36
38
  disconnectedCallback() {
37
39
  this.removeEventListener("click", this.onClick);
40
+ window.removeEventListener("resize", this.scheduleViewportPosition);
41
+ if (this.positionFrame !== null) {
42
+ cancelAnimationFrame(this.positionFrame);
43
+ this.positionFrame = null;
44
+ }
38
45
  }
39
46
  attributeChangedCallback() {
40
47
  this.render();
@@ -47,6 +54,26 @@ if (typeof HTMLElement !== "undefined") {
47
54
  dispatchTableAction(this, action);
48
55
  }
49
56
  }
57
+ positionWithinViewport = () => {
58
+ this.positionFrame = null;
59
+ const menu = this.querySelector(".ui-editor-table-context-menu");
60
+ if (!menu) return;
61
+ menu.style.translate = "";
62
+ const rect = menu.getBoundingClientRect();
63
+ const gutter = 12;
64
+ const viewportWidth = window.visualViewport?.width ?? window.innerWidth;
65
+ const viewportHeight = window.visualViewport?.height ?? window.innerHeight;
66
+ const offsetX = rect.right > viewportWidth - gutter ? viewportWidth - gutter - rect.right : Math.max(0, gutter - rect.left);
67
+ const offsetY = rect.bottom > viewportHeight - gutter ? viewportHeight - gutter - rect.bottom : Math.max(0, gutter - rect.top);
68
+ if (offsetX !== 0 || offsetY !== 0) {
69
+ menu.style.translate = `${offsetX}px ${offsetY}px`;
70
+ }
71
+ };
72
+ scheduleViewportPosition = () => {
73
+ if (!this.isConnected) return;
74
+ if (this.positionFrame !== null) cancelAnimationFrame(this.positionFrame);
75
+ this.positionFrame = requestAnimationFrame(this.positionWithinViewport);
76
+ };
50
77
  render() {
51
78
  const open = this.getBoolAttr("open");
52
79
  this.hidden = !open;
@@ -123,6 +150,7 @@ if (typeof HTMLElement !== "undefined") {
123
150
  });
124
151
  html += "</div>";
125
152
  this.innerHTML = html;
153
+ this.scheduleViewportPosition();
126
154
  }
127
155
  }
128
156
  if (typeof window !== "undefined" && !customElements.get(TAG)) {