1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-09-20 09:18:48 +02:00

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
This commit is contained in:
epriestley 2014-08-18 13:15:40 -07:00
parent 1652e07b4d
commit fe042def42
2 changed files with 76 additions and 5 deletions

View file

@ -339,11 +339,29 @@
border-top-color: {$thinblueborder}; border-top-color: {$thinblueborder};
border-radius: 0; border-radius: 0;
box-shadow: none;
-webkit-box-shadow: none;
/* Set line height explicitly so the metrics <var /> and the real textarea
are forced to the same value. */
line-height: 1.25em;
/* Prevent Safari and Chrome users from dragging the textarea any wider, /* Prevent Safari and Chrome users from dragging the textarea any wider,
because the top bar won't resize along with it. */ because the top bar won't resize along with it. */
resize: vertical; resize: vertical;
} }
var.remarkup-assist-textarea {
/* This is an invisible element used to measure the size of text in the
textarea so we can float typeaheads over the cursor position. */
display: block;
border-color: orange;
box-sizing: border-box;
padding: 4px 6px;
white-space: pre-wrap;
visibility: hidden;
}
.remarkup-assist-textarea:focus { .remarkup-assist-textarea:focus {
border: 1px solid rgba(82, 168, 236, 0.8); border: 1px solid rgba(82, 168, 236, 0.8);
} }
@ -424,11 +442,6 @@
opacity: 1.0; opacity: 1.0;
} }
.remarkup-assist-textarea {
box-shadow: none;
-webkit-box-shadow: none;
}
.remarkup-control-fullscreen-mode { .remarkup-control-fullscreen-mode {
position: fixed; position: fixed;
top: -1px; top: -1px;

View file

@ -1,5 +1,7 @@
/** /**
* @requires javelin-install * @requires javelin-install
* javelin-dom
* javelin-vector
* @provides phabricator-textareautils * @provides phabricator-textareautils
* @javelin * @javelin
*/ */
@ -44,6 +46,62 @@ JX.install('TextAreaUtils', {
area.value = v; area.value = v;
JX.TextAreaUtils.setSelectionRange(area, r.start, r.start + text.length); JX.TextAreaUtils.setSelectionRange(area, r.start, r.start + text.length);
},
/**
* Get the document pixel positions of the beginning and end of a character
* range in a textarea.
*/
getPixelDimensions: function(area, start, end) {
var v = area.value;
// We're using zero-width spaces to make sure the spans get some
// height even if there's no text in the metrics tag.
var head = v.substring(0, start);
var before = JX.$N('span', {}, '\u200b');
var body = v.substring(start, end);
var after = JX.$N('span', {}, '\u200b');
// Create a similar shadow element which we can measure.
var metrics = JX.$N(
'var',
{
className: area.className,
},
[head, before, body, after]);
// If the textarea has a scrollbar, force a scrollbar on the shadow
// element too.
if (area.scrollHeight > area.clientHeight) {
metrics.style.overflowY = 'scroll';
} }
area.parentNode.appendChild(metrics);
// Adjust the positions we read out of the document to account for the
// current scroll position of the textarea.
var metrics_pos = JX.Vector.getPos(metrics);
metrics_pos.x += area.scrollLeft;
metrics_pos.y += area.scrollTop;
var area_pos = JX.Vector.getPos(area);
var before_pos = JX.Vector.getPos(before);
var after_pos = JX.Vector.getPos(after);
JX.DOM.remove(metrics);
return {
start: {
x: area_pos.x + (before_pos.x - metrics_pos.x),
y: area_pos.y + (before_pos.y - metrics_pos.y)
},
end: {
x: area_pos.x + (after_pos.x - metrics_pos.x),
y: area_pos.y + (after_pos.y - metrics_pos.y)
}
};
}
} }
}); });