mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-23 05:50:55 +01:00
Improve drag-and-drop uploader
Summary: Make it discoverable, show uploading progress, show file thumbnails, allow you to remove files, make it a generic form component. Test Plan: Uploaded ducks Reviewed By: tomo Reviewers: aran, tomo, jungejason, tuomaspelkonen CC: anjali, aran, epriestley, tomo Differential Revision: 334
This commit is contained in:
parent
8af5bb117d
commit
109a202b6c
16 changed files with 413 additions and 121 deletions
|
@ -7,6 +7,15 @@
|
|||
*/
|
||||
|
||||
celerity_register_resource_map(array(
|
||||
'aphront-attached-file-view-css' =>
|
||||
array(
|
||||
'uri' => '/res/79bc2c2e/rsrc/css/aphront/attached-file-view.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
),
|
||||
'disk' => '/rsrc/css/aphront/attached-file-view.css',
|
||||
),
|
||||
'aphront-crumbs-view-css' =>
|
||||
array(
|
||||
'uri' => '/res/9009e6bd/rsrc/css/aphront/crumbs-view.css',
|
||||
|
@ -45,7 +54,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'aphront-form-view-css' =>
|
||||
array(
|
||||
'uri' => '/res/dadf31b1/rsrc/css/aphront/form-view.css',
|
||||
'uri' => '/res/38a347da/rsrc/css/aphront/form-view.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -270,6 +279,16 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/js/javelin/lib/behavior.js',
|
||||
),
|
||||
0 =>
|
||||
array(
|
||||
'uri' => '/res/39de799e/rsrc/js/javelin/docs/Base.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-install',
|
||||
),
|
||||
'disk' => '/rsrc/js/javelin/docs/Base.js',
|
||||
),
|
||||
'javelin-behavior-aphront-basic-tokenizer' =>
|
||||
array(
|
||||
'uri' => '/res/bce3961b/rsrc/js/application/core/behavior-tokenizer.js',
|
||||
|
@ -284,6 +303,19 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/js/application/core/behavior-tokenizer.js',
|
||||
),
|
||||
'javelin-behavior-aphront-drag-and-drop' =>
|
||||
array(
|
||||
'uri' => '/res/170115f4/rsrc/js/application/core/behavior-drag-and-drop.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
2 => 'javelin-util',
|
||||
3 => 'phabricator-drag-and-drop-file-upload',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/core/behavior-drag-and-drop.js',
|
||||
),
|
||||
'javelin-behavior-aphront-form-disable-on-submit' =>
|
||||
array(
|
||||
'uri' => '/res/6c659ede/rsrc/js/application/core/behavior-form.js',
|
||||
|
@ -477,18 +509,6 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/js/application/maniphest/behavior-transaction-controls.js',
|
||||
),
|
||||
'javelin-behavior-maniphest-transaction-drag-and-drop' =>
|
||||
array(
|
||||
'uri' => '/res/5bf1f40c/rsrc/js/application/maniphest/behavior-transaction-drag-and-drop.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
2 => 'phabricator-drag-and-drop-file-upload',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/maniphest/behavior-transaction-drag-and-drop.js',
|
||||
),
|
||||
'javelin-behavior-maniphest-transaction-expand' =>
|
||||
array(
|
||||
'uri' => '/res/966410de/rsrc/js/application/maniphest/behavior-transaction-expand.js',
|
||||
|
@ -671,16 +691,6 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/js/javelin/lib/control/typeahead/Typeahead.js',
|
||||
),
|
||||
0 =>
|
||||
array(
|
||||
'uri' => '/res/39de799e/rsrc/js/javelin/docs/Base.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-install',
|
||||
),
|
||||
'disk' => '/rsrc/js/javelin/docs/Base.js',
|
||||
),
|
||||
'javelin-typeahead-normalizer' =>
|
||||
array(
|
||||
'uri' => '/res/a5d60e3c/rsrc/js/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js',
|
||||
|
@ -888,7 +898,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'phabricator-drag-and-drop-file-upload' =>
|
||||
array(
|
||||
'uri' => '/res/711efc61/rsrc/js/application/core/DragAndDropFileUpload.js',
|
||||
'uri' => '/res/63a06ad9/rsrc/js/application/core/DragAndDropFileUpload.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -1025,7 +1035,26 @@ celerity_register_resource_map(array(
|
|||
'uri' => '/res/pkg/33f413ef/typeahead.pkg.js',
|
||||
'type' => 'js',
|
||||
),
|
||||
'ac3f56cc' =>
|
||||
'd985d27a' =>
|
||||
array (
|
||||
'name' => 'javelin.pkg.js',
|
||||
'symbols' =>
|
||||
array (
|
||||
0 => 'javelin-util',
|
||||
1 => 'javelin-install',
|
||||
2 => 'javelin-event',
|
||||
3 => 'javelin-stratcom',
|
||||
4 => 'javelin-behavior',
|
||||
5 => 'javelin-request',
|
||||
6 => 'javelin-vector',
|
||||
7 => 'javelin-dom',
|
||||
8 => 'javelin-json',
|
||||
9 => 'javelin-uri',
|
||||
),
|
||||
'uri' => '/res/pkg/d985d27a/javelin.pkg.js',
|
||||
'type' => 'js',
|
||||
),
|
||||
'e8e3f8ab' =>
|
||||
array (
|
||||
'name' => 'core.pkg.css',
|
||||
'symbols' =>
|
||||
|
@ -1046,28 +1075,9 @@ celerity_register_resource_map(array(
|
|||
13 => 'phabricator-remarkup-css',
|
||||
14 => 'syntax-highlighting-css',
|
||||
),
|
||||
'uri' => '/res/pkg/ac3f56cc/core.pkg.css',
|
||||
'uri' => '/res/pkg/e8e3f8ab/core.pkg.css',
|
||||
'type' => 'css',
|
||||
),
|
||||
'd985d27a' =>
|
||||
array (
|
||||
'name' => 'javelin.pkg.js',
|
||||
'symbols' =>
|
||||
array (
|
||||
0 => 'javelin-util',
|
||||
1 => 'javelin-install',
|
||||
2 => 'javelin-event',
|
||||
3 => 'javelin-stratcom',
|
||||
4 => 'javelin-behavior',
|
||||
5 => 'javelin-request',
|
||||
6 => 'javelin-vector',
|
||||
7 => 'javelin-dom',
|
||||
8 => 'javelin-json',
|
||||
9 => 'javelin-uri',
|
||||
),
|
||||
'uri' => '/res/pkg/d985d27a/javelin.pkg.js',
|
||||
'type' => 'js',
|
||||
),
|
||||
'ed383f69' =>
|
||||
array (
|
||||
'name' => 'differential.pkg.js',
|
||||
|
@ -1085,15 +1095,15 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'reverse' =>
|
||||
array (
|
||||
'aphront-crumbs-view-css' => 'ac3f56cc',
|
||||
'aphront-dialog-view-css' => 'ac3f56cc',
|
||||
'aphront-form-view-css' => 'ac3f56cc',
|
||||
'aphront-list-filter-view-css' => 'ac3f56cc',
|
||||
'aphront-panel-view-css' => 'ac3f56cc',
|
||||
'aphront-side-nav-view-css' => 'ac3f56cc',
|
||||
'aphront-table-view-css' => 'ac3f56cc',
|
||||
'aphront-tokenizer-control-css' => 'ac3f56cc',
|
||||
'aphront-typeahead-control-css' => 'ac3f56cc',
|
||||
'aphront-crumbs-view-css' => 'e8e3f8ab',
|
||||
'aphront-dialog-view-css' => 'e8e3f8ab',
|
||||
'aphront-form-view-css' => 'e8e3f8ab',
|
||||
'aphront-list-filter-view-css' => 'e8e3f8ab',
|
||||
'aphront-panel-view-css' => 'e8e3f8ab',
|
||||
'aphront-side-nav-view-css' => 'e8e3f8ab',
|
||||
'aphront-table-view-css' => 'e8e3f8ab',
|
||||
'aphront-tokenizer-control-css' => 'e8e3f8ab',
|
||||
'aphront-typeahead-control-css' => 'e8e3f8ab',
|
||||
'differential-changeset-view-css' => '1ac25e8a',
|
||||
'differential-core-view-css' => '1ac25e8a',
|
||||
'differential-revision-add-comment-css' => '1ac25e8a',
|
||||
|
@ -1128,11 +1138,11 @@ celerity_register_resource_map(array(
|
|||
'javelin-util' => 'd985d27a',
|
||||
'javelin-vector' => 'd985d27a',
|
||||
'javelin-workflow' => '122a6b6d',
|
||||
'phabricator-core-buttons-css' => 'ac3f56cc',
|
||||
'phabricator-core-css' => 'ac3f56cc',
|
||||
'phabricator-directory-css' => 'ac3f56cc',
|
||||
'phabricator-remarkup-css' => 'ac3f56cc',
|
||||
'phabricator-standard-page-view' => 'ac3f56cc',
|
||||
'syntax-highlighting-css' => 'ac3f56cc',
|
||||
'phabricator-core-buttons-css' => 'e8e3f8ab',
|
||||
'phabricator-core-css' => 'e8e3f8ab',
|
||||
'phabricator-directory-css' => 'e8e3f8ab',
|
||||
'phabricator-remarkup-css' => 'e8e3f8ab',
|
||||
'phabricator-standard-page-view' => 'e8e3f8ab',
|
||||
'syntax-highlighting-css' => 'e8e3f8ab',
|
||||
),
|
||||
));
|
||||
|
|
|
@ -13,6 +13,7 @@ phutil_register_library_map(array(
|
|||
'Aphront404Response' => 'aphront/response/404',
|
||||
'AphrontAjaxResponse' => 'aphront/response/ajax',
|
||||
'AphrontApplicationConfiguration' => 'aphront/applicationconfiguration',
|
||||
'AphrontAttachedFileView' => 'view/control/attachedfile',
|
||||
'AphrontController' => 'aphront/controller',
|
||||
'AphrontCrumbsView' => 'view/layout/crumbs',
|
||||
'AphrontDatabaseConnection' => 'storage/connection/base',
|
||||
|
@ -26,6 +27,7 @@ phutil_register_library_map(array(
|
|||
'AphrontFormCheckboxControl' => 'view/form/control/checkbox',
|
||||
'AphrontFormControl' => 'view/form/control/base',
|
||||
'AphrontFormDividerControl' => 'view/form/control/divider',
|
||||
'AphrontFormDragAndDropUploadControl' => 'view/form/control/draganddropupload',
|
||||
'AphrontFormFileControl' => 'view/form/control/file',
|
||||
'AphrontFormMarkupControl' => 'view/form/control/markup',
|
||||
'AphrontFormPasswordControl' => 'view/form/control/password',
|
||||
|
@ -528,6 +530,7 @@ phutil_register_library_map(array(
|
|||
'Aphront400Response' => 'AphrontResponse',
|
||||
'Aphront404Response' => 'AphrontResponse',
|
||||
'AphrontAjaxResponse' => 'AphrontResponse',
|
||||
'AphrontAttachedFileView' => 'AphrontView',
|
||||
'AphrontCrumbsView' => 'AphrontView',
|
||||
'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration',
|
||||
'AphrontDefaultApplicationController' => 'AphrontController',
|
||||
|
@ -538,6 +541,7 @@ phutil_register_library_map(array(
|
|||
'AphrontFormCheckboxControl' => 'AphrontFormControl',
|
||||
'AphrontFormControl' => 'AphrontView',
|
||||
'AphrontFormDividerControl' => 'AphrontFormControl',
|
||||
'AphrontFormDragAndDropUploadControl' => 'AphrontFormControl',
|
||||
'AphrontFormFileControl' => 'AphrontFormControl',
|
||||
'AphrontFormMarkupControl' => 'AphrontFormControl',
|
||||
'AphrontFormPasswordControl' => 'AphrontFormControl',
|
||||
|
|
|
@ -30,12 +30,13 @@ class PhabricatorFileDropUploadController extends PhabricatorFileController {
|
|||
'name' => $request->getStr('name'),
|
||||
));
|
||||
|
||||
$view = new AphrontAttachedFileView();
|
||||
$view->setFile($file);
|
||||
|
||||
return id(new AphrontAjaxResponse())->setContent(
|
||||
array(
|
||||
'name' => $file->getName(),
|
||||
'phid' => $file->getPHID(),
|
||||
'size' => $file->getByteSize(),
|
||||
'uri' => $file->getViewURI(),
|
||||
'html' => $view->render(),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
phutil_require_module('phabricator', 'aphront/response/ajax');
|
||||
phutil_require_module('phabricator', 'applications/files/controller/base');
|
||||
phutil_require_module('phabricator', 'applications/files/storage/file');
|
||||
phutil_require_module('phabricator', 'view/control/attachedfile');
|
||||
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
|
|
@ -202,6 +202,10 @@ class PhabricatorFile extends PhabricatorFileDAO {
|
|||
return PhabricatorFileURI::getViewURIForPHID($this->getPHID());
|
||||
}
|
||||
|
||||
public function getThumb60x45URI() {
|
||||
return '/file/xform/thumb-60x45/'.$this->getPHID().'/';
|
||||
}
|
||||
|
||||
public function isViewableInBrowser() {
|
||||
return ($this->getViewableMimeType() !== null);
|
||||
}
|
||||
|
|
|
@ -222,6 +222,8 @@ class ManiphestTaskDetailController extends ManiphestController {
|
|||
$draft_text = null;
|
||||
}
|
||||
|
||||
$panel_id = celerity_generate_unique_node_id();
|
||||
|
||||
$comment_form = new AphrontFormView();
|
||||
$comment_form
|
||||
->setUser($user)
|
||||
|
@ -286,12 +288,11 @@ class ManiphestTaskDetailController extends ManiphestController {
|
|||
->setValue($draft_text)
|
||||
->setID('transaction-comments'))
|
||||
->appendChild(
|
||||
id(new AphrontFormMarkupControl())
|
||||
id(new AphrontFormDragAndDropUploadControl())
|
||||
->setLabel('Attached Files')
|
||||
->setValue(
|
||||
'<div id="file-list">'.
|
||||
'None'.
|
||||
'</div>'))
|
||||
->setName('files')
|
||||
->setDragAndDropTarget($panel_id)
|
||||
->setActivatedClass('aphront-panel-view-drag-and-drop'))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue('Avast!'));
|
||||
|
@ -335,22 +336,11 @@ class ManiphestTaskDetailController extends ManiphestController {
|
|||
'map' => $control_map,
|
||||
));
|
||||
|
||||
$id = celerity_generate_unique_node_id();
|
||||
|
||||
$comment_panel = new AphrontPanelView();
|
||||
$comment_panel->appendChild($comment_form);
|
||||
$comment_panel->setID($id);
|
||||
$comment_panel->setID($panel_id);
|
||||
$comment_panel->addClass('aphront-panel-accent');
|
||||
$comment_panel->setHeader('Leap Into Action');
|
||||
|
||||
Javelin::initBehavior(
|
||||
'maniphest-transaction-drag-and-drop',
|
||||
array(
|
||||
'target' => $id,
|
||||
'activatedClass' => 'aphront-panel-view-drag-and-drop',
|
||||
'uri' => '/file/dropupload/',
|
||||
'list' => 'file-list',
|
||||
));
|
||||
$comment_panel->setHeader('Weigh In');
|
||||
|
||||
$preview_panel =
|
||||
'<div class="aphront-panel-preview">
|
||||
|
|
|
@ -21,8 +21,8 @@ phutil_require_module('phabricator', 'applications/phid/handle/data');
|
|||
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
||||
phutil_require_module('phabricator', 'view/form/base');
|
||||
phutil_require_module('phabricator', 'view/form/control/draganddropupload');
|
||||
phutil_require_module('phabricator', 'view/form/control/file');
|
||||
phutil_require_module('phabricator', 'view/form/control/markup');
|
||||
phutil_require_module('phabricator', 'view/form/control/select');
|
||||
phutil_require_module('phabricator', 'view/form/control/submit');
|
||||
phutil_require_module('phabricator', 'view/form/control/textarea');
|
||||
|
|
73
src/view/control/attachedfile/AphrontAttachedFileView.php
Normal file
73
src/view/control/attachedfile/AphrontAttachedFileView.php
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class AphrontAttachedFileView extends AphrontView {
|
||||
|
||||
private $file;
|
||||
|
||||
public function setFile(PhabricatorFile $file) {
|
||||
$this->file = $file;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
require_celerity_resource('aphront-attached-file-view-css');
|
||||
|
||||
$file = $this->file;
|
||||
$phid = $file->getPHID();
|
||||
|
||||
$thumb = phutil_render_tag(
|
||||
'img',
|
||||
array(
|
||||
'src' => $file->getThumb60x45URI(),
|
||||
'width' => 60,
|
||||
'height' => 45,
|
||||
));
|
||||
|
||||
$name = phutil_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $file->getViewURI(),
|
||||
'target' => '_blank',
|
||||
),
|
||||
phutil_escape_html($file->getName()));
|
||||
$size = number_format($file->getByteSize()).' bytes';
|
||||
|
||||
$remove = javelin_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'class' => 'button grey',
|
||||
'sigil' => 'aphront-attached-file-view-remove',
|
||||
// NOTE: Using 'ref' here instead of 'meta' because the file upload
|
||||
// endpoint doesn't receive request metadata and thus can't generate
|
||||
// a valid response with node metadata.
|
||||
'ref' => $file->getPHID(),
|
||||
),
|
||||
"\xE2\x9C\x96"); // "Heavy Multiplication X"
|
||||
|
||||
return
|
||||
'<table class="aphront-attached-file-view">
|
||||
<tr>
|
||||
<td>'.$thumb.'</td>
|
||||
<th><strong>'.$name.'</strong><br />'.$size.'</th>
|
||||
<td class="aphront-attached-file-view-remove">'.$remove.'</td>
|
||||
</tr>
|
||||
</table>';
|
||||
}
|
||||
|
||||
}
|
16
src/view/control/attachedfile/__init__.php
Normal file
16
src/view/control/attachedfile/__init__.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||
phutil_require_module('phabricator', 'view/base');
|
||||
|
||||
phutil_require_module('phutil', 'markup');
|
||||
|
||||
|
||||
phutil_require_source('AphrontAttachedFileView.php');
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class AphrontFormDragAndDropUploadControl extends AphrontFormControl {
|
||||
|
||||
private $dragAndDropTarget;
|
||||
private $activatedClass;
|
||||
|
||||
public function __construct() {
|
||||
$this->setControlID(celerity_generate_unique_node_id());
|
||||
$this->setControlStyle('display: none;');
|
||||
}
|
||||
|
||||
protected function getCustomControlClass() {
|
||||
return 'aphront-form-drag-and-drop-upload';
|
||||
}
|
||||
|
||||
public function setDragAndDropTarget($id) {
|
||||
$this->dragAndDropTarget = $id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setActivatedClass($class) {
|
||||
$this->activatedClass = $class;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function renderInput() {
|
||||
require_celerity_resource('aphront-attached-file-view-css');
|
||||
|
||||
$list_id = celerity_generate_unique_node_id();
|
||||
|
||||
$files = $this->getValue();
|
||||
$value = array();
|
||||
foreach ($files as $file) {
|
||||
$view = new AphrontAttachedFileView();
|
||||
$view->setFile($file);
|
||||
$value[$file->getPHID()] = array(
|
||||
'phid' => $file->getPHID(),
|
||||
'html' => $view->render(),
|
||||
);
|
||||
}
|
||||
|
||||
Javelin::initBehavior(
|
||||
'aphront-drag-and-drop',
|
||||
array(
|
||||
'control' => $this->getControlID(),
|
||||
'name' => $this->getName(),
|
||||
'value' => nonempty($value, null),
|
||||
'list' => $list_id,
|
||||
'uri' => '/file/dropupload/',
|
||||
'target' => $this->dragAndDropTarget,
|
||||
'activatedClass' => $this->activatedClass,
|
||||
));
|
||||
|
||||
return phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'id' => $list_id,
|
||||
'class' => 'aphront-form-drag-and-drop-file-list',
|
||||
),
|
||||
'');
|
||||
}
|
||||
|
||||
}
|
18
src/view/form/control/draganddropupload/__init__.php
Normal file
18
src/view/form/control/draganddropupload/__init__.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
||||
phutil_require_module('phabricator', 'view/control/attachedfile');
|
||||
phutil_require_module('phabricator', 'view/form/control/base');
|
||||
|
||||
phutil_require_module('phutil', 'markup');
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
||||
phutil_require_source('AphrontFormDragAndDropUploadControl.php');
|
24
webroot/rsrc/css/aphront/attached-file-view.css
Normal file
24
webroot/rsrc/css/aphront/attached-file-view.css
Normal file
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* @provides aphront-attached-file-view-css
|
||||
*/
|
||||
|
||||
.aphront-attached-file-view {
|
||||
border: 1px solid #aaaaaa;
|
||||
background: #f9f9f9;
|
||||
width: 100%;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.aphront-attached-file-view td,
|
||||
.aphront-attached-file-view th {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.aphront-attached-file-view th {
|
||||
width: 100%;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.aphront-attached-file-view-remove {
|
||||
vertical-align: middle;
|
||||
}
|
|
@ -116,3 +116,13 @@ table.aphront-form-control-checkbox-layout th {
|
|||
background: #f3f3f3;
|
||||
border: 1px solid #afafaf;
|
||||
}
|
||||
|
||||
.aphront-form-drag-and-drop-file-list {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.drag-and-drop-instructions {
|
||||
color: #333333;
|
||||
font-size: 11px;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,14 @@ JX.install('PhabricatorDragAndDropFileUpload', {
|
|||
|
||||
events : ['willUpload', 'didUpload'],
|
||||
|
||||
statics : {
|
||||
isSupported : function() {
|
||||
// TODO: Is there a better capability test for this? This seems okay in
|
||||
// Safari, Firefox and Chrome.
|
||||
return !!window.FileList;
|
||||
}
|
||||
},
|
||||
|
||||
members : {
|
||||
_node : null,
|
||||
_depth : 0,
|
||||
|
|
92
webroot/rsrc/js/application/core/behavior-drag-and-drop.js
Normal file
92
webroot/rsrc/js/application/core/behavior-drag-and-drop.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* @provides javelin-behavior-aphront-drag-and-drop
|
||||
* @requires javelin-behavior
|
||||
* javelin-dom
|
||||
* javelin-util
|
||||
* phabricator-drag-and-drop-file-upload
|
||||
*/
|
||||
|
||||
JX.behavior('aphront-drag-and-drop', function(config) {
|
||||
|
||||
// The control renders hidden by default; if we don't have support for
|
||||
// drag-and-drop just leave it hidden.
|
||||
if (!JX.PhabricatorDragAndDropFileUpload.isSupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show the control, since we have browser support.
|
||||
JX.$(config.control).style.display = '';
|
||||
|
||||
var files = config.value || {};
|
||||
var pending = 0;
|
||||
|
||||
var list = JX.$(config.list);
|
||||
|
||||
var drop = new JX.PhabricatorDragAndDropFileUpload(JX.$(config.target))
|
||||
.setActivatedClass(config.activatedClass)
|
||||
.setURI(config.uri);
|
||||
|
||||
drop.listen('willUpload', function(f) {
|
||||
pending++;
|
||||
redraw();
|
||||
});
|
||||
|
||||
drop.listen('didUpload', function(f) {
|
||||
files[f.phid] = f;
|
||||
|
||||
// This redraws "Upload complete!"
|
||||
pending--;
|
||||
redraw(true);
|
||||
|
||||
// This redraws the instructions.
|
||||
JX.defer(redraw, 1000);
|
||||
});
|
||||
|
||||
drop.start();
|
||||
redraw();
|
||||
|
||||
JX.DOM.listen(
|
||||
list,
|
||||
'click',
|
||||
'aphront-attached-file-view-remove',
|
||||
function(e) {
|
||||
e.kill();
|
||||
delete files[e.getTarget().getAttribute('ref')];
|
||||
redraw();
|
||||
});
|
||||
|
||||
function redraw(completed) {
|
||||
var items = [];
|
||||
for (var k in files) {
|
||||
var file = files[k];
|
||||
items.push(JX.$N('div', {}, JX.$H(file.html)));
|
||||
items.push(JX.$N(
|
||||
'input',
|
||||
{
|
||||
type: "hidden",
|
||||
name: config.name + "[" + file.phid + "]",
|
||||
value: file.phid
|
||||
}));
|
||||
}
|
||||
|
||||
var status;
|
||||
if (!pending) {
|
||||
if (completed) {
|
||||
status = JX.$H('<strong>Upload complete!</strong>');
|
||||
} else {
|
||||
arrow = String.fromCharCode(0x21EA);
|
||||
status = JX.$H(
|
||||
arrow + ' <strong>Drag and Drop</strong> files here to upload them.');
|
||||
}
|
||||
} else {
|
||||
status = JX.$H(
|
||||
'Uploading <strong>' + parseInt(pending, 10) + '<strong> files...');
|
||||
}
|
||||
status = JX.$N('div', {className: 'drag-and-drop-instructions'}, status);
|
||||
|
||||
items.push(status);
|
||||
JX.DOM.setContent(list, items);
|
||||
}
|
||||
|
||||
});
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
/**
|
||||
* @provides javelin-behavior-maniphest-transaction-drag-and-drop
|
||||
* @requires javelin-behavior
|
||||
* javelin-dom
|
||||
* phabricator-drag-and-drop-file-upload
|
||||
*/
|
||||
|
||||
JX.behavior('maniphest-transaction-drag-and-drop', function(config) {
|
||||
|
||||
var files = [];
|
||||
|
||||
var drop = new JX.PhabricatorDragAndDropFileUpload(JX.$(config.target))
|
||||
.setActivatedClass(config.activatedClass)
|
||||
.setURI(config.uri);
|
||||
|
||||
drop.listen('didUpload', function(f) {
|
||||
files.push(f);
|
||||
redraw();
|
||||
});
|
||||
|
||||
drop.start();
|
||||
|
||||
function redraw() {
|
||||
var items = [];
|
||||
for (var ii = 0; ii < files.length; ii++) {
|
||||
items.push(JX.$N('div', {}, files[ii].name));
|
||||
items.push(JX.$N(
|
||||
'input',
|
||||
{
|
||||
type: "hidden",
|
||||
name: "files[" + files[ii].phid + "]",
|
||||
value: files[ii].phid
|
||||
}));
|
||||
}
|
||||
JX.DOM.setContent(JX.$(config.list), items);
|
||||
}
|
||||
|
||||
});
|
||||
|
Loading…
Reference in a new issue