@threadlabs/looma 0.23.23 → 0.23.24

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.
@@ -205,6 +205,8 @@ export function createAnchoredSurface(surface, options = {}) {
205
205
  let point = null;
206
206
  let open = false;
207
207
  let frame = null;
208
+ let anchorFrame = null;
209
+ let lastAnchorRect = null;
208
210
  let abort = null;
209
211
  let sizeObserver = null;
210
212
  surface.setAttribute("popover", "manual");
@@ -218,12 +220,31 @@ export function createAnchoredSurface(surface, options = {}) {
218
220
  return;
219
221
  }
220
222
  if (!anchor) return;
221
- fallbackPosition(surface, anchor.getBoundingClientRect(), placement, gap(), viewportGap);
223
+ lastAnchorRect = anchor.getBoundingClientRect();
224
+ fallbackPosition(surface, lastAnchorRect, placement, gap(), viewportGap);
222
225
  };
223
226
  const schedule = () => {
224
227
  if (open && frame === null) frame = owner.requestAnimationFrame(position);
225
228
  };
229
+ // Resize and scroll events do not report an ancestor's transition or layout shift.
230
+ // While visible, sample the anchor and only re-position when its bounds change.
231
+ const trackAnchor = () => {
232
+ anchorFrame = null;
233
+ if (!open || !anchor || point) return;
234
+ const rect = anchor.getBoundingClientRect();
235
+ if (!lastAnchorRect || rect.left !== lastAnchorRect.left || rect.top !== lastAnchorRect.top
236
+ || rect.width !== lastAnchorRect.width || rect.height !== lastAnchorRect.height) schedule();
237
+ anchorFrame = owner.requestAnimationFrame(trackAnchor);
238
+ };
239
+ const stopTracking = () => {
240
+ if (anchorFrame !== null) owner.cancelAnimationFrame(anchorFrame);
241
+ anchorFrame = null;
242
+ lastAnchorRect = null;
243
+ };
226
244
  const syncListeners = () => {
245
+ if (open && anchor && !point) {
246
+ if (anchorFrame === null) anchorFrame = owner.requestAnimationFrame(trackAnchor);
247
+ } else stopTracking();
227
248
  const needed = open && (point || anchor);
228
249
  if (!needed) {
229
250
  abort?.abort();
@@ -251,7 +272,7 @@ export function createAnchoredSurface(surface, options = {}) {
251
272
  showAtPoint(next) { point = next; open = true; syncListeners(); show(surface); position(); schedule(); observeSize(); },
252
273
  hide() { open = false; point = null; syncListeners(); stopSize(); if (frame !== null) owner.cancelAnimationFrame(frame); frame = null; hide(surface); },
253
274
  refresh: schedule,
254
- destroy() { open = false; abort?.abort(); abort = null; stopSize(); if (frame !== null) owner.cancelAnimationFrame(frame); frame = null; hide(surface); anchor = null; },
275
+ destroy() { open = false; stopTracking(); abort?.abort(); abort = null; stopSize(); if (frame !== null) owner.cancelAnimationFrame(frame); frame = null; hide(surface); anchor = null; },
255
276
  };
256
277
  }
257
278
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threadlabs/looma",
3
- "version": "0.23.23",
3
+ "version": "0.23.24",
4
4
  "type": "module",
5
5
  "description": "Looma components as HTML, Vue, and plain DOM, with design tokens.",
6
6
  "license": "MIT",
@@ -9969,27 +9969,4 @@ html[data-ui-input-modality="touch"]
9969
9969
  width: 3px;
9970
9970
  background: var(--ui-affordance-direct-color, var(--ui-accent-solid, #5b55d6));
9971
9971
  }
9972
-
9973
- .ProseMirror .column-resize-handle::before {
9974
- content: "Drag to resize column";
9975
- position: absolute;
9976
- z-index: 4;
9977
- top: 4px;
9978
- left: calc(100% + 6px);
9979
- width: max-content;
9980
- padding: var(--ui-space-1, 4px) var(--ui-space-2, 8px);
9981
- border-radius: var(--ui-radius-sm, 4px);
9982
- background: var(--ui-text-primary, #1a1a1a);
9983
- color: var(--ui-surface-elevated, #fff);
9984
- font-size: var(--ui-font-size-xs, 0.75rem);
9985
- line-height: var(--ui-line-height-md, 1.4);
9986
- opacity: 0;
9987
- transform: none;
9988
- transition: opacity var(--ui-affordance-motion, var(--ui-motion-fast, 120ms)) var(--ui-motion-ease, ease);
9989
- pointer-events: none;
9990
- }
9991
-
9992
- .ProseMirror .column-resize-handle:hover::before {
9993
- opacity: 1;
9994
- }
9995
9972
  /*$vite$:1*/
@@ -1758,6 +1758,8 @@ var LoomaEditor = defineComponent({
1758
1758
  const tooltipLabel = ref("");
1759
1759
  const tooltipShortcut = ref("");
1760
1760
  const tooltipOpen = ref(false);
1761
+ const resizeHintFor = ref("");
1762
+ let resizeHintSequence = 0;
1761
1763
  let tooltipTimer = 0;
1762
1764
  const clearTooltipTimer = () => {
1763
1765
  if (tooltipTimer) window.clearTimeout(tooltipTimer);
@@ -2879,6 +2881,11 @@ var LoomaEditor = defineComponent({
2879
2881
  nextTick(updateTableUi);
2880
2882
  };
2881
2883
  const onEditorPointerOver = (event) => {
2884
+ const handle = event.pointerType !== "touch" && event.target instanceof HTMLElement ? event.target.closest(".column-resize-handle") : null;
2885
+ if (handle) {
2886
+ if (!handle.id) handle.id = `${toolbarScope}-resize-${++resizeHintSequence}`;
2887
+ resizeHintFor.value = handle.id;
2888
+ }
2882
2889
  const cell = event.target instanceof HTMLElement ? event.target.closest("td, th") : null;
2883
2890
  if (!cell) return;
2884
2891
  if (cell === hoveredTableCell) return;
@@ -2890,6 +2897,8 @@ var LoomaEditor = defineComponent({
2890
2897
  });
2891
2898
  };
2892
2899
  const onEditorPointerOut = (event) => {
2900
+ const handle = event.target instanceof HTMLElement ? event.target.closest(".column-resize-handle") : null;
2901
+ if (handle && (!(event.relatedTarget instanceof Node) || !handle.contains(event.relatedTarget))) resizeHintFor.value = "";
2893
2902
  const fromCell = event.target instanceof HTMLElement ? event.target.closest("td, th") : null;
2894
2903
  if (!fromCell || fromCell !== hoveredTableCell) return;
2895
2904
  const next = event.relatedTarget;
@@ -2898,6 +2907,7 @@ var LoomaEditor = defineComponent({
2898
2907
  nextTick(updateTableUi);
2899
2908
  };
2900
2909
  const onEditorPointerLeave = (event) => {
2910
+ resizeHintFor.value = "";
2901
2911
  const next = event.relatedTarget;
2902
2912
  if (next instanceof Node && (tableOverlayShell.value?.contains(next) || tableToolbarShell.value?.contains(next) || tableMenuShell.value?.contains(next))) return;
2903
2913
  if (!hoveredTableCell) return;
@@ -3111,6 +3121,13 @@ var LoomaEditor = defineComponent({
3111
3121
  onDblclick: onImageDoubleClick,
3112
3122
  onKeydown: onImageKeyDown
3113
3123
  }, [
3124
+ resizeHintFor.value ? h(Tooltip, {
3125
+ for: resizeHintFor.value,
3126
+ open: true,
3127
+ trigger: "focus",
3128
+ placement: "top",
3129
+ inverse: true
3130
+ }, () => "Drag to resize column") : null,
3114
3131
  instance && props.editable && !mobile.value && (props.toolbarMode === "bubble" || props.toolbarMode === "popover" || props.toolbarMode === "contextual") ? h(BubbleMenu, {
3115
3132
  editor: instance,
3116
3133
  pluginKey: "looma-text-formatting-menu",