diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index b533d8d712..04685c0873 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -72,7 +72,7 @@ celerity_register_resource_map(array( ), 'aphront-form-view-css' => array( - 'uri' => '/res/38bc1599/rsrc/css/aphront/form-view.css', + 'uri' => '/res/aec38c95/rsrc/css/aphront/form-view.css', 'type' => 'css', 'requires' => array( @@ -699,6 +699,18 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/core/behavior-error-log.js', ), + 'javelin-behavior-fancy-datepicker' => + array( + 'uri' => '/res/b2d89e4c/rsrc/js/application/core/behavior-fancy-datepicker.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-util', + 2 => 'javelin-dom', + ), + 'disk' => '/rsrc/js/application/core/behavior-fancy-datepicker.js', + ), 'javelin-behavior-files-drag-and-drop' => array( 'uri' => '/res/3a7a2a8a/rsrc/js/application/core/behavior-files-drag-and-drop.js', @@ -2022,7 +2034,7 @@ celerity_register_resource_map(array( ), array( 'packages' => array( - '943d4357' => + '803864a4' => array( 'name' => 'core.pkg.css', 'symbols' => @@ -2047,7 +2059,7 @@ celerity_register_resource_map(array( 17 => 'aphront-pager-view-css', 18 => 'phabricator-transaction-view-css', ), - 'uri' => '/res/pkg/943d4357/core.pkg.css', + 'uri' => '/res/pkg/803864a4/core.pkg.css', 'type' => 'css', ), '21d01ed8' => @@ -2194,17 +2206,17 @@ celerity_register_resource_map(array( 'reverse' => array( 'aphront-attached-file-view-css' => 'f45e0b15', - 'aphront-crumbs-view-css' => '943d4357', - 'aphront-dialog-view-css' => '943d4357', - 'aphront-form-view-css' => '943d4357', + 'aphront-crumbs-view-css' => '803864a4', + 'aphront-dialog-view-css' => '803864a4', + 'aphront-form-view-css' => '803864a4', 'aphront-headsup-action-list-view-css' => '18be02e0', - 'aphront-list-filter-view-css' => '943d4357', - 'aphront-pager-view-css' => '943d4357', - 'aphront-panel-view-css' => '943d4357', - 'aphront-side-nav-view-css' => '943d4357', - 'aphront-table-view-css' => '943d4357', - 'aphront-tokenizer-control-css' => '943d4357', - 'aphront-typeahead-control-css' => '943d4357', + 'aphront-list-filter-view-css' => '803864a4', + 'aphront-pager-view-css' => '803864a4', + 'aphront-panel-view-css' => '803864a4', + 'aphront-side-nav-view-css' => '803864a4', + 'aphront-table-view-css' => '803864a4', + 'aphront-tokenizer-control-css' => '803864a4', + 'aphront-typeahead-control-css' => '803864a4', 'differential-changeset-view-css' => '18be02e0', 'differential-core-view-css' => '18be02e0', 'differential-inline-comment-editor' => 'b2139675', @@ -2262,23 +2274,23 @@ celerity_register_resource_map(array( 'maniphest-task-detail-css' => 'f45e0b15', 'maniphest-task-summary-css' => 'f45e0b15', 'maniphest-transaction-detail-css' => 'f45e0b15', - 'phabricator-app-buttons-css' => '943d4357', + 'phabricator-app-buttons-css' => '803864a4', 'phabricator-content-source-view-css' => '18be02e0', - 'phabricator-core-buttons-css' => '943d4357', - 'phabricator-core-css' => '943d4357', - 'phabricator-directory-css' => '943d4357', + 'phabricator-core-buttons-css' => '803864a4', + 'phabricator-core-css' => '803864a4', + 'phabricator-directory-css' => '803864a4', 'phabricator-drag-and-drop-file-upload' => 'b2139675', 'phabricator-dropdown-menu' => '21d01ed8', - 'phabricator-jump-nav' => '943d4357', + 'phabricator-jump-nav' => '803864a4', 'phabricator-keyboard-shortcut' => '21d01ed8', 'phabricator-keyboard-shortcut-manager' => '21d01ed8', 'phabricator-menu-item' => '21d01ed8', 'phabricator-object-selector-css' => '18be02e0', 'phabricator-paste-file-upload' => '21d01ed8', - 'phabricator-remarkup-css' => '943d4357', + 'phabricator-remarkup-css' => '803864a4', 'phabricator-shaped-request' => 'b2139675', - 'phabricator-standard-page-view' => '943d4357', - 'phabricator-transaction-view-css' => '943d4357', - 'syntax-highlighting-css' => '943d4357', + 'phabricator-standard-page-view' => '803864a4', + 'phabricator-transaction-view-css' => '803864a4', + 'syntax-highlighting-css' => '803864a4', ), )); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index e7f99dd42e..fb26bfa7ce 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -31,6 +31,7 @@ phutil_register_library_map(array( 'AphrontFileResponse' => 'aphront/response/file', 'AphrontFormCheckboxControl' => 'view/form/control/checkbox', 'AphrontFormControl' => 'view/form/control/base', + 'AphrontFormDateControl' => 'view/form/control/date', 'AphrontFormDividerControl' => 'view/form/control/divider', 'AphrontFormDragAndDropUploadControl' => 'view/form/control/draganddropupload', 'AphrontFormFileControl' => 'view/form/control/file', @@ -610,6 +611,7 @@ phutil_register_library_map(array( 'PhabricatorFlagListController' => 'applications/flag/controller/list', 'PhabricatorFlagListView' => 'applications/flag/view/list', 'PhabricatorFlagQuery' => 'applications/flag/query/flag', + 'PhabricatorFormExample' => 'applications/uiexample/examples/form', 'PhabricatorGarbageCollectorDaemon' => 'infrastructure/daemon/garbagecollector', 'PhabricatorGoodForNothingWorker' => 'infrastructure/daemon/workers/worker/goodfornothing', 'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/selector', @@ -976,6 +978,7 @@ phutil_register_library_map(array( 'AphrontFileResponse' => 'AphrontResponse', 'AphrontFormCheckboxControl' => 'AphrontFormControl', 'AphrontFormControl' => 'AphrontView', + 'AphrontFormDateControl' => 'AphrontFormControl', 'AphrontFormDividerControl' => 'AphrontFormControl', 'AphrontFormDragAndDropUploadControl' => 'AphrontFormControl', 'AphrontFormFileControl' => 'AphrontFormControl', @@ -1447,6 +1450,7 @@ phutil_register_library_map(array( 'PhabricatorFlagEditController' => 'PhabricatorFlagController', 'PhabricatorFlagListController' => 'PhabricatorFlagController', 'PhabricatorFlagListView' => 'AphrontView', + 'PhabricatorFormExample' => 'PhabricatorUIExample', 'PhabricatorGarbageCollectorDaemon' => 'PhabricatorDaemon', 'PhabricatorGoodForNothingWorker' => 'PhabricatorWorker', 'PhabricatorHelpController' => 'PhabricatorController', diff --git a/src/applications/uiexample/examples/form/PhabricatorFormExample.php b/src/applications/uiexample/examples/form/PhabricatorFormExample.php new file mode 100644 index 0000000000..0cb2fdf040 --- /dev/null +++ b/src/applications/uiexample/examples/form/PhabricatorFormExample.php @@ -0,0 +1,53 @@ +AphrontFormView to render forms.'; + } + + public function renderExample() { + $request = $this->getRequest(); + $user = $request->getUser(); + + $date = id(new AphrontFormDateControl()) + ->setUser($user) + ->setName('date') + ->setLabel('Date'); + + $date->readValueFromRequest($request); + + $form = id(new AphrontFormView()) + ->setUser($user) + ->appendChild($date) + ->appendChild( + id(new AphrontFormSubmitControl()) + ->setValue('Submit')); + + $panel = new AphrontPanelView(); + $panel->setHeader('Form'); + $panel->appendChild($form); + + return $panel; + } +} diff --git a/src/applications/uiexample/examples/form/__init__.php b/src/applications/uiexample/examples/form/__init__.php new file mode 100644 index 0000000000..011652fa76 --- /dev/null +++ b/src/applications/uiexample/examples/form/__init__.php @@ -0,0 +1,18 @@ +user = $user; + return $this; + } + + public function readValueFromRequest(AphrontRequest $request) { + + $day = $request->getInt($this->getDayInputName()); + $month = $request->getInt($this->getMonthInputName()); + $year = $request->getInt($this->getYearInputName()); + + $err = $this->getError(); + + if ($day || $month || $year) { + + // Assume invalid. + $err = 'Invalid'; + + $tz = new DateTimeZone('UTC'); + try { + $date = new DateTime("{$year}-{$month}-{$day} 12:00:00 AM", $tz); + $value = $date->format('Y-m-d'); + if ($value) { + $this->setValue($value); + $err = null; + } + } catch (Exception $ex) { + // Ignore, already handled. + } + } + + $this->setError($err); + + return $err; + } + + public function getValue() { + if (!parent::getValue()) { + $this->setValue($this->formatTime(time(), 'Y-m-d')); + } + return parent::getValue(); + } + + + protected function getCustomControlClass() { + return 'aphront-form-control-date'; + } + + private function getMinYear() { + $cur_year = $this->formatTime( + time(), + 'Y'); + $val_year = $this->getYearInputValue(); + + return min($cur_year, $val_year) - 3; + } + + private function getMaxYear() { + $cur_year = $this->formatTime( + time(), + 'Y'); + $val_year = $this->getYearInputValue(); + + return max($cur_year, $val_year) + 3; + } + + private function getDayInputValue() { + return (int)idx(explode('-', $this->getValue()), 2); + } + + private function getMonthInputValue() { + return (int)idx(explode('-', $this->getValue()), 1); + } + + private function getYearInputValue() { + return (int)idx(explode('-', $this->getValue()), 0); + } + + private function formatTime($epoch, $fmt) { + return phabricator_format_local_time( + $epoch, + $this->user, + $fmt); + } + + private function getDayInputName() { + return $this->getName().'_d'; + } + + private function getMonthInputName() { + return $this->getName().'_m'; + } + + private function getYearInputName() { + return $this->getName().'_y'; + } + + protected function renderInput() { + $min_year = $this->getMinYear(); + $max_year = $this->getMaxYear(); + + $days = range(1, 31); + $days = array_combine($days, $days); + + $months = array( + 1 => 'Jan', + 2 => 'Feb', + 3 => 'Mar', + 4 => 'Apr', + 5 => 'May', + 6 => 'Jun', + 7 => 'Jul', + 8 => 'Aug', + 9 => 'Sep', + 10 => 'Oct', + 11 => 'Nov', + 12 => 'Dec', + ); + + $years = range($this->getMinYear(), $this->getMaxYear()); + $years = array_combine($years, $years); + + $days_sel = AphrontFormSelectControl::renderSelectTag( + $this->getDayInputValue(), + $days, + array( + 'name' => $this->getDayInputName(), + 'sigil' => 'day-input', + )); + + $months_sel = AphrontFormSelectControl::renderSelectTag( + $this->getMonthInputValue(), + $months, + array( + 'name' => $this->getMonthInputName(), + 'sigil' => 'month-input', + )); + + $years_sel = AphrontFormSelectControl::renderSelectTag( + $this->getYearInputValue(), + $years, + array( + 'name' => $this->getYearInputName(), + 'sigil' => 'year-input', + )); + + $cal_icon = javelin_render_tag( + 'a', + array( + 'href' => '#', + 'class' => 'calendar-button', + 'sigil' => 'calendar-button', + ), + ''); + + $id = celerity_generate_unique_node_id(); + + Javelin::initBehavior( + 'fancy-datepicker', + array( + 'root' => $id, + )); + + return javelin_render_tag( + 'div', + array( + 'id' => $id, + 'class' => 'aphront-form-date-container', + ), + self::renderSingleView( + array( + $days_sel, + $months_sel, + $years_sel, + $cal_icon, + ))); + } + +} diff --git a/src/view/form/control/date/__init__.php b/src/view/form/control/date/__init__.php new file mode 100644 index 0000000000..b7eacce30c --- /dev/null +++ b/src/view/form/control/date/__init__.php @@ -0,0 +1,19 @@ += 12) { + value_m -= 12; + value_y += 1; + } else if (value_m < 0) { + value_m += 12; + value_y -= 1; + } + value_m = value_m + 1; + break; + case 'd': + // User clicked a day. + value_d = parseInt(p[1]); + write_date(); + + // Wait a moment to close the selector so they can see the effect + // of their action. + setTimeout(JX.bind(null, onclose, e), 150); + break; + } + + render(); + }); + +});