react-grab 0.0.14 → 0.0.15

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.
Files changed (2) hide show
  1. package/dist/index.global.js +284 -146
  2. package/package.json +1 -1
@@ -129,6 +129,7 @@ var ReactGrab = (function (exports) {
129
129
  var watchKeyHeldFor = (key, duration, onHeld) => {
130
130
  let timeoutId = null;
131
131
  let unsubscribe = null;
132
+ const watchStartTime = Date.now();
132
133
  const cleanup = () => {
133
134
  if (timeoutId !== null) {
134
135
  clearTimeout(timeoutId);
@@ -153,29 +154,9 @@ var ReactGrab = (function (exports) {
153
154
  }
154
155
  return checkSingleKeyPressed(key, pressedKeys);
155
156
  };
156
- const getKeyFromTimestamps = (keyToFind, timestamps) => {
157
- if (keyToFind.length === 1) {
158
- return timestamps.get(keyToFind.toLowerCase()) || timestamps.get(keyToFind.toUpperCase());
159
- }
160
- return timestamps.get(keyToFind);
161
- };
162
- const getEarliestPressTime = (timestamps) => {
163
- const keysToInspect = Array.isArray(key) ? key : [key];
164
- let earliest;
165
- for (const keyFromCombo of keysToInspect) {
166
- const timestamp = getKeyFromTimestamps(keyFromCombo, timestamps);
167
- if (timestamp === void 0) {
168
- return void 0;
169
- }
170
- if (earliest === void 0 || timestamp < earliest) {
171
- earliest = timestamp;
172
- }
173
- }
174
- return earliest;
175
- };
176
157
  const scheduleCallback = () => {
177
158
  const state = libStore.getState();
178
- const { keyPressTimestamps, pressedKeys } = state;
159
+ const { pressedKeys } = state;
179
160
  if (!checkAllKeysPressed(pressedKeys)) {
180
161
  if (timeoutId !== null) {
181
162
  clearTimeout(timeoutId);
@@ -183,15 +164,7 @@ var ReactGrab = (function (exports) {
183
164
  }
184
165
  return;
185
166
  }
186
- const earliestPressTime = getEarliestPressTime(keyPressTimestamps);
187
- if (earliestPressTime === void 0) {
188
- if (timeoutId !== null) {
189
- clearTimeout(timeoutId);
190
- timeoutId = null;
191
- }
192
- return;
193
- }
194
- const elapsed = Date.now() - earliestPressTime;
167
+ const elapsed = Date.now() - watchStartTime;
195
168
  const remaining = duration - elapsed;
196
169
  if (remaining <= 0) {
197
170
  onHeld();
@@ -2469,7 +2442,6 @@ ${error.stack}`;
2469
2442
  var INDICATOR_CLAMP_PADDING_PX = 4;
2470
2443
  var INDICATOR_SUCCESS_VISIBLE_MS = 1500;
2471
2444
  var INDICATOR_FADE_MS = 200;
2472
- var INDICATOR_TOTAL_HIDE_DELAY_MS = INDICATOR_SUCCESS_VISIBLE_MS + INDICATOR_FADE_MS;
2473
2445
  var lerp = (start, end, factor) => {
2474
2446
  return start + (end - start) * factor;
2475
2447
  };
@@ -2554,6 +2526,30 @@ ${error.stack}`;
2554
2526
  }
2555
2527
  };
2556
2528
  };
2529
+ var createGrabbedOverlay = (root, selection) => {
2530
+ const element = document.createElement("div");
2531
+ element.style.position = "fixed";
2532
+ element.style.top = `${selection.y}px`;
2533
+ element.style.left = `${selection.x}px`;
2534
+ element.style.width = `${selection.width}px`;
2535
+ element.style.height = `${selection.height}px`;
2536
+ element.style.borderRadius = selection.borderRadius;
2537
+ element.style.transform = selection.transform;
2538
+ element.style.pointerEvents = "none";
2539
+ element.style.border = "1px solid rgb(210, 57, 192)";
2540
+ element.style.backgroundColor = "rgba(210, 57, 192, 0.2)";
2541
+ element.style.zIndex = "2147483646";
2542
+ element.style.boxSizing = "border-box";
2543
+ element.style.transition = "opacity 0.3s ease-out";
2544
+ element.style.opacity = "1";
2545
+ root.appendChild(element);
2546
+ requestAnimationFrame(() => {
2547
+ element.style.opacity = "0";
2548
+ });
2549
+ setTimeout(() => {
2550
+ element.remove();
2551
+ }, 300);
2552
+ };
2557
2553
  var createSpinner = () => {
2558
2554
  const spinner = document.createElement("span");
2559
2555
  spinner.style.display = "inline-block";
@@ -2599,12 +2595,12 @@ ${error.stack}`;
2599
2595
  indicator.style.whiteSpace = "nowrap";
2600
2596
  return indicator;
2601
2597
  };
2602
- var showLabel = (selectionLeftPx, selectionTopPx, tagName) => {
2598
+ var showLabel = (root, selectionLeftPx, selectionTopPx, tagName) => {
2603
2599
  let indicator = activeIndicator;
2604
2600
  let isNewIndicator = false;
2605
2601
  if (!indicator) {
2606
2602
  indicator = createIndicator();
2607
- document.body.appendChild(indicator);
2603
+ root.appendChild(indicator);
2608
2604
  activeIndicator = indicator;
2609
2605
  isNewIndicator = true;
2610
2606
  isProcessing = false;
@@ -2658,22 +2654,47 @@ ${error.stack}`;
2658
2654
  }
2659
2655
  };
2660
2656
  var isProcessing = false;
2661
- var updateLabelToProcessing = () => {
2662
- if (!activeIndicator || isProcessing) return () => {
2657
+ var activeGrabbedIndicators = /* @__PURE__ */ new Set();
2658
+ var updateLabelToProcessing = (root, selectionLeftPx, selectionTopPx) => {
2659
+ const indicator = createIndicator();
2660
+ indicator.style.zIndex = "2147483648";
2661
+ root.appendChild(indicator);
2662
+ activeGrabbedIndicators.add(indicator);
2663
+ const positionIndicator = () => {
2664
+ if (selectionLeftPx === void 0 || selectionTopPx === void 0) return;
2665
+ const indicatorRect = indicator.getBoundingClientRect();
2666
+ const viewportWidthPx = window.innerWidth;
2667
+ const viewportHeightPx = window.innerHeight;
2668
+ let indicatorLeftPx = Math.round(selectionLeftPx);
2669
+ let indicatorTopPx = Math.round(selectionTopPx) - indicatorRect.height - LABEL_OFFSET_PX;
2670
+ const CLAMPED_PADDING = INDICATOR_CLAMP_PADDING_PX;
2671
+ const minLeft = VIEWPORT_MARGIN_PX;
2672
+ const minTop = VIEWPORT_MARGIN_PX;
2673
+ const maxLeft = viewportWidthPx - indicatorRect.width - VIEWPORT_MARGIN_PX;
2674
+ const maxTop = viewportHeightPx - indicatorRect.height - VIEWPORT_MARGIN_PX;
2675
+ const willClampLeft = indicatorLeftPx < minLeft;
2676
+ const willClampTop = indicatorTopPx < minTop;
2677
+ const isClamped = willClampLeft || willClampTop;
2678
+ indicatorLeftPx = Math.max(minLeft, Math.min(indicatorLeftPx, maxLeft));
2679
+ indicatorTopPx = Math.max(minTop, Math.min(indicatorTopPx, maxTop));
2680
+ if (isClamped) {
2681
+ indicatorLeftPx += CLAMPED_PADDING;
2682
+ indicatorTopPx += CLAMPED_PADDING;
2683
+ }
2684
+ indicator.style.left = `${indicatorLeftPx}px`;
2685
+ indicator.style.top = `${indicatorTopPx}px`;
2686
+ indicator.style.right = "auto";
2663
2687
  };
2664
- isProcessing = true;
2665
- const indicator = activeIndicator;
2666
- indicator.innerHTML = "";
2667
2688
  const loadingSpinner = createSpinner();
2668
2689
  const labelText = document.createElement("span");
2669
2690
  labelText.textContent = "Grabbing\u2026";
2670
2691
  indicator.appendChild(loadingSpinner);
2671
2692
  indicator.appendChild(labelText);
2693
+ positionIndicator();
2694
+ requestAnimationFrame(() => {
2695
+ indicator.style.opacity = "1";
2696
+ });
2672
2697
  return (tagName) => {
2673
- if (!activeIndicator) {
2674
- isProcessing = false;
2675
- return;
2676
- }
2677
2698
  indicator.textContent = "";
2678
2699
  const checkmarkIcon = document.createElement("span");
2679
2700
  checkmarkIcon.textContent = "\u2713";
@@ -2689,14 +2710,14 @@ ${error.stack}`;
2689
2710
  newLabelText.appendChild(tagNameMonospace);
2690
2711
  indicator.appendChild(checkmarkIcon);
2691
2712
  indicator.appendChild(newLabelText);
2713
+ requestAnimationFrame(() => {
2714
+ positionIndicator();
2715
+ });
2692
2716
  setTimeout(() => {
2693
2717
  indicator.style.opacity = "0";
2694
2718
  setTimeout(() => {
2695
2719
  indicator.remove();
2696
- if (activeIndicator === indicator) {
2697
- activeIndicator = null;
2698
- }
2699
- isProcessing = false;
2720
+ activeGrabbedIndicators.delete(indicator);
2700
2721
  }, INDICATOR_FADE_MS);
2701
2722
  }, INDICATOR_SUCCESS_VISIBLE_MS);
2702
2723
  };
@@ -2708,6 +2729,88 @@ ${error.stack}`;
2708
2729
  }
2709
2730
  isProcessing = false;
2710
2731
  };
2732
+ var cleanupGrabbedIndicators = () => {
2733
+ for (const indicator of activeGrabbedIndicators) {
2734
+ indicator.remove();
2735
+ }
2736
+ activeGrabbedIndicators.clear();
2737
+ };
2738
+ var activeProgressIndicator = null;
2739
+ var createProgressIndicatorElement = () => {
2740
+ const container = document.createElement("div");
2741
+ container.style.position = "fixed";
2742
+ container.style.zIndex = "2147483647";
2743
+ container.style.pointerEvents = "none";
2744
+ container.style.opacity = "0";
2745
+ container.style.transition = "opacity 0.1s ease-in-out";
2746
+ const progressBarContainer = document.createElement("div");
2747
+ progressBarContainer.style.width = "32px";
2748
+ progressBarContainer.style.height = "2px";
2749
+ progressBarContainer.style.backgroundColor = "rgba(178, 28, 142, 0.2)";
2750
+ progressBarContainer.style.borderRadius = "1px";
2751
+ progressBarContainer.style.overflow = "hidden";
2752
+ progressBarContainer.style.position = "relative";
2753
+ const progressBarFill = document.createElement("div");
2754
+ progressBarFill.style.width = "0%";
2755
+ progressBarFill.style.height = "100%";
2756
+ progressBarFill.style.backgroundColor = "#b21c8e";
2757
+ progressBarFill.style.borderRadius = "1px";
2758
+ progressBarFill.style.transition = "width 0.05s linear";
2759
+ progressBarFill.setAttribute("data-progress-fill", "true");
2760
+ progressBarContainer.appendChild(progressBarFill);
2761
+ container.appendChild(progressBarContainer);
2762
+ return container;
2763
+ };
2764
+ var showProgressIndicator = (root, progress, mouseX, mouseY) => {
2765
+ if (!activeProgressIndicator) {
2766
+ activeProgressIndicator = createProgressIndicatorElement();
2767
+ root.appendChild(activeProgressIndicator);
2768
+ requestAnimationFrame(() => {
2769
+ if (activeProgressIndicator) {
2770
+ activeProgressIndicator.style.opacity = "1";
2771
+ }
2772
+ });
2773
+ }
2774
+ const indicator = activeProgressIndicator;
2775
+ const indicatorRect = indicator.getBoundingClientRect();
2776
+ const viewportWidth = window.innerWidth;
2777
+ const viewportHeight = window.innerHeight;
2778
+ const CURSOR_OFFSET = 14;
2779
+ const VIEWPORT_MARGIN = 8;
2780
+ let indicatorLeft = mouseX - indicatorRect.width / 2;
2781
+ let indicatorTop = mouseY + CURSOR_OFFSET;
2782
+ if (indicatorTop + indicatorRect.height + VIEWPORT_MARGIN > viewportHeight) {
2783
+ indicatorTop = mouseY - indicatorRect.height - CURSOR_OFFSET;
2784
+ }
2785
+ indicatorTop = Math.max(
2786
+ VIEWPORT_MARGIN,
2787
+ Math.min(indicatorTop, viewportHeight - indicatorRect.height - VIEWPORT_MARGIN)
2788
+ );
2789
+ indicatorLeft = Math.max(
2790
+ VIEWPORT_MARGIN,
2791
+ Math.min(indicatorLeft, viewportWidth - indicatorRect.width - VIEWPORT_MARGIN)
2792
+ );
2793
+ indicator.style.top = `${indicatorTop}px`;
2794
+ indicator.style.left = `${indicatorLeft}px`;
2795
+ const progressFill = indicator.querySelector(
2796
+ "[data-progress-fill]"
2797
+ );
2798
+ if (progressFill) {
2799
+ const percentage = Math.min(100, Math.max(0, progress * 100));
2800
+ progressFill.style.width = `${percentage}%`;
2801
+ }
2802
+ };
2803
+ var hideProgressIndicator = () => {
2804
+ if (activeProgressIndicator) {
2805
+ activeProgressIndicator.style.opacity = "0";
2806
+ setTimeout(() => {
2807
+ if (activeProgressIndicator) {
2808
+ activeProgressIndicator.remove();
2809
+ activeProgressIndicator = null;
2810
+ }
2811
+ }, 100);
2812
+ }
2813
+ };
2711
2814
 
2712
2815
  // src/utils/copy-text.ts
2713
2816
  var IS_NAVIGATOR_CLIPBOARD_AVAILABLE = typeof window !== "undefined" && window.navigator.clipboard && window.isSecureContext;
@@ -2773,19 +2876,6 @@ ${error.stack}`;
2773
2876
  return root;
2774
2877
  };
2775
2878
 
2776
- // src/utils/schedule-run-when-idle.ts
2777
- var scheduleRunWhenIdle = (callback) => {
2778
- if ("scheduler" in globalThis) {
2779
- return globalThis.scheduler.postTask(callback, {
2780
- priority: "background"
2781
- });
2782
- }
2783
- if ("requestIdleCallback" in window) {
2784
- return requestIdleCallback(callback);
2785
- }
2786
- return setTimeout(callback, 0);
2787
- };
2788
-
2789
2879
  // src/utils/store.ts
2790
2880
  var createStore = (initializer) => {
2791
2881
  const subscriberMap = /* @__PURE__ */ new Map();
@@ -2857,29 +2947,7 @@ ${error.stack}`;
2857
2947
  return store;
2858
2948
  };
2859
2949
 
2860
- // src/utils/throttle.ts
2861
- var throttle = (fn, delay) => {
2862
- let timeout = null;
2863
- const throttled = (...args) => {
2864
- if (timeout) {
2865
- return;
2866
- }
2867
- timeout = window.setTimeout(() => {
2868
- fn(...args);
2869
- timeout = null;
2870
- }, delay);
2871
- };
2872
- throttled.cancel = () => {
2873
- if (timeout) {
2874
- window.clearTimeout(timeout);
2875
- timeout = null;
2876
- }
2877
- };
2878
- return throttled;
2879
- };
2880
-
2881
2950
  // src/index.ts
2882
- var THROTTLE_DELAY = 16;
2883
2951
  var libStore = createStore(() => ({
2884
2952
  keyPressTimestamps: /* @__PURE__ */ new Map(),
2885
2953
  mouseX: -1e3,
@@ -2899,7 +2967,10 @@ ${error.stack}`;
2899
2967
  const root = mountRoot();
2900
2968
  const selectionOverlay = createSelectionOverlay(root);
2901
2969
  let hoveredElement = null;
2970
+ let lastGrabbedElement = null;
2902
2971
  let isCopying = false;
2972
+ let progressAnimationFrame = null;
2973
+ let progressStartTime = null;
2903
2974
  const checkIsActivationHotkeyPressed = () => {
2904
2975
  if (Array.isArray(resolvedOptions.hotkey)) {
2905
2976
  for (const key of resolvedOptions.hotkey) {
@@ -2911,6 +2982,31 @@ ${error.stack}`;
2911
2982
  }
2912
2983
  return isKeyPressed(resolvedOptions.hotkey);
2913
2984
  };
2985
+ const updateProgressIndicator = () => {
2986
+ if (progressStartTime === null) return;
2987
+ const elapsed = Date.now() - progressStartTime;
2988
+ const progress = Math.min(1, elapsed / resolvedOptions.keyHoldDuration);
2989
+ const { mouseX, mouseY } = libStore.getState();
2990
+ showProgressIndicator(root, progress, mouseX, mouseY);
2991
+ if (progress < 1) {
2992
+ progressAnimationFrame = requestAnimationFrame(updateProgressIndicator);
2993
+ }
2994
+ };
2995
+ const startProgressTracking = () => {
2996
+ if (progressAnimationFrame !== null) return;
2997
+ progressStartTime = Date.now();
2998
+ const { mouseX, mouseY } = libStore.getState();
2999
+ showProgressIndicator(root, 0, mouseX, mouseY);
3000
+ progressAnimationFrame = requestAnimationFrame(updateProgressIndicator);
3001
+ };
3002
+ const stopProgressTracking = () => {
3003
+ if (progressAnimationFrame !== null) {
3004
+ cancelAnimationFrame(progressAnimationFrame);
3005
+ progressAnimationFrame = null;
3006
+ }
3007
+ progressStartTime = null;
3008
+ hideProgressIndicator();
3009
+ };
2914
3010
  let cleanupActivationHotkeyWatcher = null;
2915
3011
  const handleKeyStateChange = (pressedKeys) => {
2916
3012
  const { overlayMode } = libStore.getState();
@@ -2945,6 +3041,7 @@ ${error.stack}`;
2945
3041
  cleanupActivationHotkeyWatcher();
2946
3042
  cleanupActivationHotkeyWatcher = null;
2947
3043
  }
3044
+ stopProgressTracking();
2948
3045
  return;
2949
3046
  }
2950
3047
  const isActivationHotkeyPressed = checkIsActivationHotkeyPressed();
@@ -2959,9 +3056,11 @@ ${error.stack}`;
2959
3056
  overlayMode: "hidden"
2960
3057
  }));
2961
3058
  }
3059
+ stopProgressTracking();
2962
3060
  return;
2963
3061
  }
2964
3062
  if (overlayMode === "hidden" && !cleanupActivationHotkeyWatcher) {
3063
+ startProgressTracking();
2965
3064
  cleanupActivationHotkeyWatcher = watchKeyHeldFor(
2966
3065
  resolvedOptions.hotkey,
2967
3066
  resolvedOptions.keyHoldDuration,
@@ -2970,6 +3069,7 @@ ${error.stack}`;
2970
3069
  ...state,
2971
3070
  overlayMode: "visible"
2972
3071
  }));
3072
+ stopProgressTracking();
2973
3073
  cleanupActivationHotkeyWatcher = null;
2974
3074
  }
2975
3075
  );
@@ -2979,13 +3079,23 @@ ${error.stack}`;
2979
3079
  handleKeyStateChange,
2980
3080
  (state) => state.pressedKeys
2981
3081
  );
2982
- const handleMouseMove = throttle((event) => {
2983
- libStore.setState((state) => ({
2984
- ...state,
2985
- mouseX: event.clientX,
2986
- mouseY: event.clientY
2987
- }));
2988
- }, THROTTLE_DELAY);
3082
+ let mouseMoveScheduled = false;
3083
+ let pendingMouseX = -1e3;
3084
+ let pendingMouseY = -1e3;
3085
+ const handleMouseMove = (event) => {
3086
+ pendingMouseX = event.clientX;
3087
+ pendingMouseY = event.clientY;
3088
+ if (mouseMoveScheduled) return;
3089
+ mouseMoveScheduled = true;
3090
+ requestAnimationFrame(() => {
3091
+ mouseMoveScheduled = false;
3092
+ libStore.setState((state) => ({
3093
+ ...state,
3094
+ mouseX: pendingMouseX,
3095
+ mouseY: pendingMouseY
3096
+ }));
3097
+ });
3098
+ };
2989
3099
  const handleMouseDown = (event) => {
2990
3100
  if (event.button !== 0) {
2991
3101
  return;
@@ -3002,8 +3112,15 @@ ${error.stack}`;
3002
3112
  overlayMode: "copying"
3003
3113
  }));
3004
3114
  };
3115
+ const handleVisibilityChange = () => {
3116
+ if (document.hidden) {
3117
+ cleanupGrabbedIndicators();
3118
+ hideLabel();
3119
+ }
3120
+ };
3005
3121
  window.addEventListener("mousemove", handleMouseMove);
3006
3122
  window.addEventListener("mousedown", handleMouseDown);
3123
+ document.addEventListener("visibilitychange", handleVisibilityChange);
3007
3124
  const cleanupTrackHotkeys = trackHotkeys();
3008
3125
  const getElementAtPosition = (x, y) => {
3009
3126
  const elements = document.elementsFromPoint(x, y);
@@ -3020,70 +3137,75 @@ ${error.stack}`;
3020
3137
  return null;
3021
3138
  };
3022
3139
  const handleCopy = async (element) => {
3023
- const cleanupIndicator = updateLabelToProcessing();
3140
+ const tagName = (element.tagName || "").toLowerCase();
3141
+ const rect = element.getBoundingClientRect();
3142
+ const cleanupIndicator = updateLabelToProcessing(root, rect.left, rect.top);
3024
3143
  try {
3025
- const stack = await getStack(element);
3026
3144
  const htmlSnippet = getHTMLSnippet(element);
3027
- let text = htmlSnippet;
3145
+ await copyTextToClipboard(
3146
+ `
3147
+
3148
+ <referenced_element>
3149
+ ${htmlSnippet}
3150
+ </referenced_element>`
3151
+ );
3152
+ cleanupIndicator(tagName);
3153
+ const stack = await getStack(element);
3028
3154
  if (stack) {
3029
3155
  const filteredStack = filterStack(stack);
3030
3156
  const serializedStack = serializeStack(filteredStack);
3031
- text = `${htmlSnippet}
3157
+ const fullText = `${htmlSnippet}
3032
3158
 
3033
3159
  Component owner stack:
3034
3160
  ${serializedStack}`;
3035
- }
3036
- await copyTextToClipboard(
3037
- `
3161
+ await copyTextToClipboard(
3162
+ `
3038
3163
 
3039
3164
  <referenced_element>
3040
- ${text}
3165
+ ${fullText}
3041
3166
  </referenced_element>`
3042
- );
3043
- const tagName = (element.tagName || "").toLowerCase();
3044
- cleanupIndicator(tagName);
3167
+ ).catch(() => {
3168
+ });
3169
+ }
3045
3170
  } catch {
3046
- cleanupIndicator();
3171
+ cleanupIndicator(tagName);
3047
3172
  }
3048
3173
  };
3049
- const handleRender = throttle((state) => {
3174
+ const handleRender = (state) => {
3050
3175
  const { mouseX, mouseY, overlayMode } = state;
3051
3176
  if (overlayMode === "hidden") {
3052
3177
  if (selectionOverlay.isVisible()) {
3053
3178
  selectionOverlay.hide();
3054
- if (!isCopying) {
3055
- hideLabel();
3056
- }
3057
- hoveredElement = null;
3058
3179
  }
3180
+ if (!isCopying) {
3181
+ hideLabel();
3182
+ }
3183
+ hoveredElement = null;
3184
+ lastGrabbedElement = null;
3059
3185
  return;
3060
3186
  }
3061
3187
  if (overlayMode === "copying" && hoveredElement) {
3062
- const computedStyle2 = window.getComputedStyle(hoveredElement);
3063
- const rect2 = hoveredElement.getBoundingClientRect();
3064
- selectionOverlay.update({
3065
- borderRadius: computedStyle2.borderRadius || "0px",
3066
- height: rect2.height,
3067
- transform: computedStyle2.transform || "none",
3068
- width: rect2.width,
3069
- x: rect2.left,
3070
- y: rect2.top
3071
- });
3072
- if (!selectionOverlay.isVisible()) {
3073
- selectionOverlay.show();
3074
- }
3075
3188
  if (!isCopying) {
3076
3189
  isCopying = true;
3190
+ lastGrabbedElement = hoveredElement;
3191
+ const computedStyle2 = window.getComputedStyle(hoveredElement);
3192
+ const rect2 = hoveredElement.getBoundingClientRect();
3193
+ createGrabbedOverlay(root, {
3194
+ borderRadius: computedStyle2.borderRadius || "0px",
3195
+ height: rect2.height,
3196
+ transform: computedStyle2.transform || "none",
3197
+ width: rect2.width,
3198
+ x: rect2.left,
3199
+ y: rect2.top
3200
+ });
3077
3201
  void handleCopy(hoveredElement).finally(() => {
3078
- libStore.setState((state2) => ({
3079
- ...state2,
3080
- overlayMode: "hidden"
3081
- }));
3082
- selectionOverlay.hide();
3083
- window.setTimeout(() => {
3084
- isCopying = false;
3085
- }, INDICATOR_TOTAL_HIDE_DELAY_MS);
3202
+ isCopying = false;
3086
3203
  });
3204
+ const isStillPressed = checkIsActivationHotkeyPressed();
3205
+ libStore.setState((state2) => ({
3206
+ ...state2,
3207
+ overlayMode: isStillPressed ? "visible" : "hidden"
3208
+ }));
3087
3209
  }
3088
3210
  return;
3089
3211
  }
@@ -3091,13 +3213,26 @@ ${text}
3091
3213
  if (!element) {
3092
3214
  if (selectionOverlay.isVisible()) {
3093
3215
  selectionOverlay.hide();
3094
- if (!isCopying) {
3095
- hideLabel();
3096
- }
3216
+ }
3217
+ if (!isCopying) {
3218
+ hideLabel();
3097
3219
  }
3098
3220
  hoveredElement = null;
3099
3221
  return;
3100
3222
  }
3223
+ if (lastGrabbedElement && element !== lastGrabbedElement) {
3224
+ lastGrabbedElement = null;
3225
+ }
3226
+ if (element === lastGrabbedElement) {
3227
+ if (selectionOverlay.isVisible()) {
3228
+ selectionOverlay.hide();
3229
+ }
3230
+ if (!isCopying) {
3231
+ hideLabel();
3232
+ }
3233
+ hoveredElement = element;
3234
+ return;
3235
+ }
3101
3236
  const tagName = (element.tagName || "").toLowerCase();
3102
3237
  hoveredElement = element;
3103
3238
  const rect = element.getBoundingClientRect();
@@ -3115,35 +3250,38 @@ ${text}
3115
3250
  if (!selectionOverlay.isVisible()) {
3116
3251
  selectionOverlay.show();
3117
3252
  }
3118
- showLabel(rect.left, rect.top, tagName);
3119
- }, 10);
3120
- const cleanupRenderSubscription = libStore.subscribe((state) => {
3121
- scheduleRunWhenIdle(() => {
3122
- handleRender(state);
3253
+ showLabel(root, rect.left, rect.top, tagName);
3254
+ };
3255
+ let renderScheduled = false;
3256
+ const scheduleRender = () => {
3257
+ if (renderScheduled) return;
3258
+ renderScheduled = true;
3259
+ requestAnimationFrame(() => {
3260
+ renderScheduled = false;
3261
+ handleRender(libStore.getState());
3123
3262
  });
3263
+ };
3264
+ const cleanupRenderSubscription = libStore.subscribe(() => {
3265
+ scheduleRender();
3124
3266
  });
3125
- let timeout = null;
3126
- const render = () => {
3127
- timeout = window.setTimeout(() => {
3128
- scheduleRunWhenIdle(() => {
3129
- handleRender(libStore.getState());
3130
- render();
3131
- });
3132
- }, 100);
3267
+ const continuousRender = () => {
3268
+ scheduleRender();
3269
+ requestAnimationFrame(continuousRender);
3133
3270
  };
3134
- render();
3271
+ continuousRender();
3135
3272
  return () => {
3136
3273
  window.removeEventListener("mousemove", handleMouseMove);
3137
3274
  window.removeEventListener("mousedown", handleMouseDown);
3275
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
3138
3276
  cleanupTrackHotkeys();
3139
3277
  cleanupRenderSubscription();
3140
3278
  cleanupKeyStateChangeSubscription();
3141
- if (timeout) {
3142
- window.clearTimeout(timeout);
3143
- }
3144
3279
  if (cleanupActivationHotkeyWatcher) {
3145
3280
  cleanupActivationHotkeyWatcher();
3146
3281
  }
3282
+ stopProgressTracking();
3283
+ cleanupGrabbedIndicators();
3284
+ hideLabel();
3147
3285
  };
3148
3286
  };
3149
3287
  if (typeof window !== "undefined" && typeof document !== "undefined") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-grab",
3
- "version": "0.0.14",
3
+ "version": "0.0.15",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/aidenybai/react-grab#readme",