@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add a Javascript method to find the pixel position of a range in a textarea

Summary:
Ref T3725. This might eventually allow us to do `@username` typeaheads in textareas.

Javascript!!!

Test Plan:
Dumped this into console and got a "<<<" at the caret position in Safari, Firefox and Chrome.

```
setInterval(function() {

var area = JX.$('comment-content');
var r = JX.TextAreaUtils.getSelectionRange(area);
var d = JX.TextAreaUtils.getPixelDimensions(area, r.start, r.end);

JX.log(d);

try {
JX.DOM.remove(JX.$('ptr'));
} catch (_) {}

document.body.appendChild(
JX.$N(
'div',
{id: "ptr", style: { position: 'absolute', left: d.start.x + 'px', top: d.start.y + 'px', zIndex: 9999, border: '2px solid red' }},
'<<<'));
}, 1000);
```

Reviewers: chad, btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T3725

Differential Revision: https://secure.phabricator.com/D10280

+76 -5
+18 -5
webroot/rsrc/css/core/remarkup.css
··· 339 339 border-top-color: {$thinblueborder}; 340 340 border-radius: 0; 341 341 342 + box-shadow: none; 343 + -webkit-box-shadow: none; 344 + 345 + /* Set line height explicitly so the metrics <var /> and the real textarea 346 + are forced to the same value. */ 347 + line-height: 1.25em; 348 + 342 349 /* Prevent Safari and Chrome users from dragging the textarea any wider, 343 350 because the top bar won't resize along with it. */ 344 351 resize: vertical; 352 + } 353 + 354 + var.remarkup-assist-textarea { 355 + /* This is an invisible element used to measure the size of text in the 356 + textarea so we can float typeaheads over the cursor position. */ 357 + display: block; 358 + border-color: orange; 359 + box-sizing: border-box; 360 + padding: 4px 6px; 361 + white-space: pre-wrap; 362 + visibility: hidden; 345 363 } 346 364 347 365 .remarkup-assist-textarea:focus { ··· 422 440 .jx-order-mask { 423 441 background: #ffffff; 424 442 opacity: 1.0; 425 - } 426 - 427 - .remarkup-assist-textarea { 428 - box-shadow: none; 429 - -webkit-box-shadow: none; 430 443 } 431 444 432 445 .remarkup-control-fullscreen-mode {
+58
webroot/rsrc/js/core/TextAreaUtils.js
··· 1 1 /** 2 2 * @requires javelin-install 3 + * javelin-dom 4 + * javelin-vector 3 5 * @provides phabricator-textareautils 4 6 * @javelin 5 7 */ ··· 44 46 area.value = v; 45 47 46 48 JX.TextAreaUtils.setSelectionRange(area, r.start, r.start + text.length); 49 + }, 50 + 51 + /** 52 + * Get the document pixel positions of the beginning and end of a character 53 + * range in a textarea. 54 + */ 55 + getPixelDimensions: function(area, start, end) { 56 + var v = area.value; 57 + 58 + // We're using zero-width spaces to make sure the spans get some 59 + // height even if there's no text in the metrics tag. 60 + 61 + var head = v.substring(0, start); 62 + var before = JX.$N('span', {}, '\u200b'); 63 + var body = v.substring(start, end); 64 + var after = JX.$N('span', {}, '\u200b'); 65 + 66 + // Create a similar shadow element which we can measure. 67 + var metrics = JX.$N( 68 + 'var', 69 + { 70 + className: area.className, 71 + }, 72 + [head, before, body, after]); 73 + 74 + // If the textarea has a scrollbar, force a scrollbar on the shadow 75 + // element too. 76 + if (area.scrollHeight > area.clientHeight) { 77 + metrics.style.overflowY = 'scroll'; 78 + } 79 + 80 + area.parentNode.appendChild(metrics); 81 + 82 + // Adjust the positions we read out of the document to account for the 83 + // current scroll position of the textarea. 84 + var metrics_pos = JX.Vector.getPos(metrics); 85 + metrics_pos.x += area.scrollLeft; 86 + metrics_pos.y += area.scrollTop; 87 + 88 + var area_pos = JX.Vector.getPos(area); 89 + var before_pos = JX.Vector.getPos(before); 90 + var after_pos = JX.Vector.getPos(after); 91 + 92 + JX.DOM.remove(metrics); 93 + 94 + return { 95 + start: { 96 + x: area_pos.x + (before_pos.x - metrics_pos.x), 97 + y: area_pos.y + (before_pos.y - metrics_pos.y) 98 + }, 99 + end: { 100 + x: area_pos.x + (after_pos.x - metrics_pos.x), 101 + y: area_pos.y + (after_pos.y - metrics_pos.y) 102 + } 103 + }; 47 104 } 105 + 48 106 } 49 107 });