mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40:55 +01:00
Put inline previews in remarkup textareas
Summary: Ref T3967. This gives us a reasonable baseline for doing remarkup previews inline in all contexts, and works in weird/constrained context including: - inline comments; - conpherence; and - custom fields. It would be nicer to go beyond this in contexts like Phame posts, but this is a start, at least. Test Plan: {F1040877} {F1040878} Reviewers: chad Reviewed By: chad Maniphest Tasks: T3967 Differential Revision: https://secure.phabricator.com/D14855
This commit is contained in:
parent
551732b962
commit
f5ff10fe28
7 changed files with 195 additions and 31 deletions
|
@ -7,8 +7,8 @@
|
|||
*/
|
||||
return array(
|
||||
'names' => array(
|
||||
'core.pkg.css' => '4cf32aa0',
|
||||
'core.pkg.js' => '821768c9',
|
||||
'core.pkg.css' => '1a2d5480',
|
||||
'core.pkg.js' => 'cf262309',
|
||||
'darkconsole.pkg.js' => 'e7393ebb',
|
||||
'differential.pkg.css' => '2de124c9',
|
||||
'differential.pkg.js' => '64e69521',
|
||||
|
@ -104,7 +104,7 @@ return array(
|
|||
'rsrc/css/application/tokens/tokens.css' => '3d0f239e',
|
||||
'rsrc/css/application/uiexample/example.css' => '528b19de',
|
||||
'rsrc/css/core/core.css' => 'a76cefc9',
|
||||
'rsrc/css/core/remarkup.css' => '275e362f',
|
||||
'rsrc/css/core/remarkup.css' => '72024fc6',
|
||||
'rsrc/css/core/syntax.css' => '9fd11da8',
|
||||
'rsrc/css/core/z-index.css' => '57ddcaa2',
|
||||
'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa',
|
||||
|
@ -487,7 +487,7 @@ return array(
|
|||
'rsrc/js/core/behavior-object-selector.js' => '49b73b36',
|
||||
'rsrc/js/core/behavior-oncopy.js' => '2926fff2',
|
||||
'rsrc/js/core/behavior-phabricator-nav.js' => '56a1ca03',
|
||||
'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '461fd61b',
|
||||
'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => 'ecddcbe2',
|
||||
'rsrc/js/core/behavior-refresh-csrf.js' => 'ab2f381b',
|
||||
'rsrc/js/core/behavior-remarkup-preview.js' => 'f7379f45',
|
||||
'rsrc/js/core/behavior-reorder-applications.js' => '76b9fc3e',
|
||||
|
@ -640,7 +640,7 @@ return array(
|
|||
'javelin-behavior-phabricator-notification-example' => '8ce821c5',
|
||||
'javelin-behavior-phabricator-object-selector' => '49b73b36',
|
||||
'javelin-behavior-phabricator-oncopy' => '2926fff2',
|
||||
'javelin-behavior-phabricator-remarkup-assist' => '461fd61b',
|
||||
'javelin-behavior-phabricator-remarkup-assist' => 'ecddcbe2',
|
||||
'javelin-behavior-phabricator-reveal-content' => '60821bc7',
|
||||
'javelin-behavior-phabricator-search-typeahead' => '048330fa',
|
||||
'javelin-behavior-phabricator-show-older-transactions' => 'dbbf48b6',
|
||||
|
@ -759,7 +759,7 @@ return array(
|
|||
'phabricator-object-selector-css' => '85ee8ce6',
|
||||
'phabricator-phtize' => 'd254d646',
|
||||
'phabricator-prefab' => '666c80c5',
|
||||
'phabricator-remarkup-css' => '275e362f',
|
||||
'phabricator-remarkup-css' => '72024fc6',
|
||||
'phabricator-search-results-css' => '7dea472c',
|
||||
'phabricator-shaped-request' => '7cbe244b',
|
||||
'phabricator-side-menu-view-css' => 'bec2458e',
|
||||
|
@ -1100,15 +1100,6 @@ return array(
|
|||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
),
|
||||
'461fd61b' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'phabricator-phtize',
|
||||
'phabricator-textareautils',
|
||||
'javelin-workflow',
|
||||
'javelin-vector',
|
||||
),
|
||||
'469c0d9e' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -1961,6 +1952,15 @@ return array(
|
|||
'phabricator-phtize',
|
||||
'javelin-dom',
|
||||
),
|
||||
'ecddcbe2' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'phabricator-phtize',
|
||||
'phabricator-textareautils',
|
||||
'javelin-workflow',
|
||||
'javelin-vector',
|
||||
),
|
||||
'edd1ba66' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
|
|
@ -1644,6 +1644,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationTransactionNoEffectResponse' => 'applications/transactions/response/PhabricatorApplicationTransactionNoEffectResponse.php',
|
||||
'PhabricatorApplicationTransactionPublishWorker' => 'applications/transactions/worker/PhabricatorApplicationTransactionPublishWorker.php',
|
||||
'PhabricatorApplicationTransactionQuery' => 'applications/transactions/query/PhabricatorApplicationTransactionQuery.php',
|
||||
'PhabricatorApplicationTransactionRemarkupPreviewController' => 'applications/transactions/controller/PhabricatorApplicationTransactionRemarkupPreviewController.php',
|
||||
'PhabricatorApplicationTransactionReplyHandler' => 'applications/transactions/replyhandler/PhabricatorApplicationTransactionReplyHandler.php',
|
||||
'PhabricatorApplicationTransactionResponse' => 'applications/transactions/response/PhabricatorApplicationTransactionResponse.php',
|
||||
'PhabricatorApplicationTransactionShowOlderController' => 'applications/transactions/controller/PhabricatorApplicationTransactionShowOlderController.php',
|
||||
|
@ -5760,6 +5761,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationTransactionNoEffectResponse' => 'AphrontProxyResponse',
|
||||
'PhabricatorApplicationTransactionPublishWorker' => 'PhabricatorWorker',
|
||||
'PhabricatorApplicationTransactionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorApplicationTransactionRemarkupPreviewController' => 'PhabricatorApplicationTransactionController',
|
||||
'PhabricatorApplicationTransactionReplyHandler' => 'PhabricatorMailReplyHandler',
|
||||
'PhabricatorApplicationTransactionResponse' => 'AphrontProxyResponse',
|
||||
'PhabricatorApplicationTransactionShowOlderController' => 'PhabricatorApplicationTransactionController',
|
||||
|
|
|
@ -33,6 +33,8 @@ final class PhabricatorTransactionsApplication extends PhabricatorApplication {
|
|||
=> 'PhabricatorApplicationTransactionShowOlderController',
|
||||
'(?P<value>old|new)/(?<phid>[^/]+)/'
|
||||
=> 'PhabricatorApplicationTransactionValueController',
|
||||
'remarkuppreview/'
|
||||
=> 'PhabricatorApplicationTransactionRemarkupPreviewController',
|
||||
'editengine/' => array(
|
||||
$this->getQueryRoutePattern()
|
||||
=> 'PhabricatorEditEngineListController',
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorApplicationTransactionRemarkupPreviewController
|
||||
extends PhabricatorApplicationTransactionController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$corpus = $request->getStr('corpus');
|
||||
|
||||
$remarkup = new PHUIRemarkupView($viewer, $corpus);
|
||||
|
||||
$content = array(
|
||||
'content' => hsprintf('%s', $remarkup),
|
||||
);
|
||||
|
||||
return id(new AphrontAjaxResponse())
|
||||
->setContent($content);
|
||||
}
|
||||
|
||||
}
|
|
@ -42,6 +42,8 @@ final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl {
|
|||
));
|
||||
}
|
||||
|
||||
$root_id = celerity_generate_unique_node_id();
|
||||
|
||||
Javelin::initBehavior(
|
||||
'phabricator-remarkup-assist',
|
||||
array(
|
||||
|
@ -56,6 +58,7 @@ final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl {
|
|||
'URL' => pht('URL'),
|
||||
),
|
||||
'disabled' => $this->getDisabled(),
|
||||
'rootID' => $root_id,
|
||||
));
|
||||
Javelin::initBehavior('phabricator-tooltips', array());
|
||||
|
||||
|
@ -114,11 +117,22 @@ final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl {
|
|||
);
|
||||
}
|
||||
|
||||
$actions['fa-eye'] = array(
|
||||
'tip' => pht('Preview'),
|
||||
'align' => 'right',
|
||||
);
|
||||
|
||||
$actions[] = array(
|
||||
'spacer' => true,
|
||||
'align' => 'right',
|
||||
);
|
||||
|
||||
$actions['fa-life-bouy'] = array(
|
||||
'tip' => pht('Help'),
|
||||
'align' => 'right',
|
||||
'href' => PhabricatorEnv::getDoclink('Remarkup Reference'),
|
||||
);
|
||||
'tip' => pht('Help'),
|
||||
'align' => 'right',
|
||||
'href' => PhabricatorEnv::getDoclink('Remarkup Reference'),
|
||||
);
|
||||
|
||||
|
||||
if (!$this->disableFullScreen) {
|
||||
$actions[] = array(
|
||||
|
@ -230,6 +244,7 @@ final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl {
|
|||
array(
|
||||
'sigil' => 'remarkup-assist-control',
|
||||
'class' => $this->getDisabled() ? 'disabled-control' : null,
|
||||
'id' => $root_id,
|
||||
),
|
||||
array(
|
||||
$buttons,
|
||||
|
|
|
@ -498,6 +498,7 @@ var.remarkup-assist-textarea {
|
|||
height: auto;
|
||||
border-width: 1px 0 0 0;
|
||||
outline: none;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.phabricator-image-macro-hero {
|
||||
|
@ -523,3 +524,35 @@ var.remarkup-assist-textarea {
|
|||
background-color: {$lightviolet};
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.remarkup-inline-preview {
|
||||
display: block;
|
||||
position: relative;
|
||||
background: #fff;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
border: 1px solid {$sky};
|
||||
resize: vertical;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.remarkup-control-fullscreen-mode .remarkup-inline-preview {
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.remarkup-inline-preview * {
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.remarkup-assist-button.preview-active {
|
||||
background: {$sky};
|
||||
}
|
||||
|
||||
.remarkup-assist-button.preview-active .phui-icon-view {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.remarkup-assist-button.preview-active:hover .phui-icon-view {
|
||||
color: {$lightsky};
|
||||
}
|
||||
|
|
|
@ -11,9 +11,12 @@
|
|||
|
||||
JX.behavior('phabricator-remarkup-assist', function(config) {
|
||||
var pht = JX.phtize(config.pht);
|
||||
var root = JX.$(config.rootID);
|
||||
var area = JX.DOM.find(root, 'textarea');
|
||||
|
||||
var edit_mode = 'normal';
|
||||
var edit_root = null;
|
||||
var preview = null;
|
||||
|
||||
function set_edit_mode(root, mode) {
|
||||
if (mode == edit_mode) {
|
||||
|
@ -26,7 +29,16 @@ JX.behavior('phabricator-remarkup-assist', function(config) {
|
|||
JX.DOM.alterClass(edit_root, 'remarkup-control-fullscreen-mode', false);
|
||||
JX.DOM.alterClass(document.body, 'remarkup-fullscreen-mode', false);
|
||||
}
|
||||
JX.DOM.find(edit_root, 'textarea').style.height = '';
|
||||
|
||||
area.style.height = '';
|
||||
|
||||
// If we're in preview mode, kick the preview back down to default
|
||||
// size.
|
||||
if (preview) {
|
||||
JX.DOM.show(area);
|
||||
resize_preview();
|
||||
JX.DOM.hide(area);
|
||||
}
|
||||
}
|
||||
|
||||
edit_root = root;
|
||||
|
@ -36,10 +48,21 @@ JX.behavior('phabricator-remarkup-assist', function(config) {
|
|||
if (mode == 'fa-arrows-alt') {
|
||||
JX.DOM.alterClass(edit_root, 'remarkup-control-fullscreen-mode', true);
|
||||
JX.DOM.alterClass(document.body, 'remarkup-fullscreen-mode', true);
|
||||
|
||||
// If we're in preview mode, expand the preview to full-size.
|
||||
if (preview) {
|
||||
JX.DOM.show(area);
|
||||
}
|
||||
|
||||
resizearea();
|
||||
|
||||
if (preview) {
|
||||
resize_preview();
|
||||
JX.DOM.hide(area);
|
||||
}
|
||||
}
|
||||
|
||||
JX.DOM.focus(JX.DOM.find(edit_root, 'textarea'));
|
||||
JX.DOM.focus(area);
|
||||
}
|
||||
|
||||
function resizearea() {
|
||||
|
@ -54,8 +77,6 @@ JX.behavior('phabricator-remarkup-assist', function(config) {
|
|||
// "top" and "bottom", and height "auto" renders as two lines high. Force
|
||||
// it to the correct height with Javascript.
|
||||
|
||||
var area = JX.DOM.find(edit_root, 'textarea');
|
||||
|
||||
var v = JX.Vector.getViewport();
|
||||
v.x = null;
|
||||
v.y -= 26;
|
||||
|
@ -65,7 +86,6 @@ JX.behavior('phabricator-remarkup-assist', function(config) {
|
|||
|
||||
JX.Stratcom.listen('resize', null, resizearea);
|
||||
|
||||
|
||||
JX.Stratcom.listen('keydown', null, function(e) {
|
||||
if (e.getSpecialKey() != 'esc') {
|
||||
return;
|
||||
|
@ -115,7 +135,7 @@ JX.behavior('phabricator-remarkup-assist', function(config) {
|
|||
return sel.join('\n' + ch);
|
||||
}
|
||||
|
||||
function assist(area, action, root) {
|
||||
function assist(area, action, root, button) {
|
||||
// If the user has some text selected, we'll try to use that (for example,
|
||||
// if they have a word selected and want to bold it). Otherwise we'll insert
|
||||
// generic text.
|
||||
|
@ -182,11 +202,81 @@ JX.behavior('phabricator-remarkup-assist', function(config) {
|
|||
set_edit_mode(root, 'fa-arrows-alt');
|
||||
}
|
||||
break;
|
||||
case 'fa-eye':
|
||||
if (!preview) {
|
||||
preview = JX.$N(
|
||||
'div',
|
||||
{
|
||||
className: 'remarkup-inline-preview'
|
||||
},
|
||||
null);
|
||||
|
||||
area.parentNode.insertBefore(preview, area);
|
||||
JX.DOM.alterClass(button, 'preview-active', true);
|
||||
resize_preview();
|
||||
JX.DOM.hide(area);
|
||||
|
||||
update_preview();
|
||||
} else {
|
||||
JX.DOM.show(area);
|
||||
resize_preview(true);
|
||||
JX.DOM.remove(preview);
|
||||
preview = null;
|
||||
|
||||
JX.DOM.alterClass(button, 'preview-active', false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
JX.Stratcom.listen(
|
||||
['click'],
|
||||
function resize_preview(restore) {
|
||||
if (!preview) {
|
||||
return;
|
||||
}
|
||||
|
||||
var src;
|
||||
var dst;
|
||||
|
||||
if (restore) {
|
||||
src = preview;
|
||||
dst = area;
|
||||
} else {
|
||||
src = area;
|
||||
dst = preview;
|
||||
}
|
||||
|
||||
var d = JX.Vector.getDim(src);
|
||||
d.x = null;
|
||||
d.setDim(dst);
|
||||
}
|
||||
|
||||
function update_preview() {
|
||||
var value = area.value;
|
||||
|
||||
var data = {
|
||||
corpus: value
|
||||
};
|
||||
|
||||
var onupdate = function(r) {
|
||||
if (area.value !== value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!preview) {
|
||||
return;
|
||||
}
|
||||
|
||||
JX.DOM.setContent(preview, JX.$H(r.content).getFragment());
|
||||
};
|
||||
|
||||
new JX.Workflow('/transactions/remarkuppreview/', data)
|
||||
.setHandler(onupdate)
|
||||
.start();
|
||||
}
|
||||
|
||||
JX.DOM.listen(
|
||||
root,
|
||||
'click',
|
||||
'remarkup-assist',
|
||||
function(e) {
|
||||
var data = e.getNodeData('remarkup-assist');
|
||||
|
@ -200,10 +290,7 @@ JX.behavior('phabricator-remarkup-assist', function(config) {
|
|||
return;
|
||||
}
|
||||
|
||||
var root = e.getNode('remarkup-assist-control');
|
||||
var area = JX.DOM.find(root, 'textarea');
|
||||
|
||||
assist(area, data.action, root);
|
||||
assist(area, data.action, root, e.getNode('remarkup-assist'));
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue