diff --git a/resources/celerity/map.php b/resources/celerity/map.php index e0f7c26960..7905323a81 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -117,8 +117,8 @@ return array( 'rsrc/css/layout/phabricator-source-code-view.css' => 'cbeef983', 'rsrc/css/phui/calendar/phui-calendar-day.css' => 'f15bb6d6', 'rsrc/css/phui/calendar/phui-calendar-list.css' => '5d89cd71', - 'rsrc/css/phui/calendar/phui-calendar-month.css' => 'f3bb2030', - 'rsrc/css/phui/calendar/phui-calendar.css' => '3354bbd6', + 'rsrc/css/phui/calendar/phui-calendar-month.css' => '29a5ef75', + 'rsrc/css/phui/calendar/phui-calendar.css' => 'daadaf39', 'rsrc/css/phui/phui-action-list.css' => 'c5eba19d', 'rsrc/css/phui/phui-action-panel.css' => '91c7b835', 'rsrc/css/phui/phui-badge.css' => '3baef8db', @@ -364,6 +364,7 @@ return array( 'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18', 'rsrc/js/application/calendar/behavior-day-view.js' => '4b3c4443', 'rsrc/js/application/calendar/behavior-event-all-day.js' => '937bb700', + 'rsrc/js/application/calendar/behavior-month-view.js' => 'fe33e256', 'rsrc/js/application/calendar/behavior-recurring-edit.js' => '5f1c4d5f', 'rsrc/js/application/config/behavior-reorder-fields.js' => 'b6993408', 'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => '01774ab2', @@ -590,6 +591,7 @@ return array( 'javelin-behavior-audit-preview' => 'd835b03a', 'javelin-behavior-badge-view' => '8ff5e24c', 'javelin-behavior-bulk-job-reload' => 'edf8a145', + 'javelin-behavior-calendar-month-view' => 'fe33e256', 'javelin-behavior-choose-control' => '327a00d1', 'javelin-behavior-comment-actions' => '06460e71', 'javelin-behavior-config-reorder-fields' => 'b6993408', @@ -825,10 +827,10 @@ return array( 'phui-big-info-view-css' => 'bd903741', 'phui-box-css' => '5c8387cf', 'phui-button-css' => '4a5fbe3d', - 'phui-calendar-css' => '3354bbd6', + 'phui-calendar-css' => 'daadaf39', 'phui-calendar-day-css' => 'f15bb6d6', 'phui-calendar-list-css' => '5d89cd71', - 'phui-calendar-month-css' => 'f3bb2030', + 'phui-calendar-month-css' => '29a5ef75', 'phui-chart-css' => '6bf6f78e', 'phui-crumbs-view-css' => 'b4fa5755', 'phui-curtain-view-css' => '7148ae25', diff --git a/src/view/phui/calendar/PHUICalendarListView.php b/src/view/phui/calendar/PHUICalendarListView.php index cff2dcf8a5..d84c0b0bc3 100644 --- a/src/view/phui/calendar/PHUICalendarListView.php +++ b/src/view/phui/calendar/PHUICalendarListView.php @@ -32,7 +32,11 @@ final class PHUICalendarListView extends AphrontTagView { protected function getTagAttributes() { require_celerity_resource('phui-calendar-css'); require_celerity_resource('phui-calendar-list-css'); - return array('class' => 'phui-calendar-event-list'); + + return array( + 'sigil' => 'calendar-event-list', + 'class' => 'phui-calendar-event-list', + ); } protected function getTagContent() { diff --git a/src/view/phui/calendar/PHUICalendarMonthView.php b/src/view/phui/calendar/PHUICalendarMonthView.php index f3f90c5a14..7f60ffd740 100644 --- a/src/view/phui/calendar/PHUICalendarMonthView.php +++ b/src/view/phui/calendar/PHUICalendarMonthView.php @@ -53,6 +53,8 @@ final class PHUICalendarMonthView extends AphrontView { public function render() { $viewer = $this->getViewer(); + Javelin::initBehavior('calendar-month-view'); + $events = msort($this->events, 'getEpochStart'); $days = $this->getDatesInMonth(); @@ -111,32 +113,50 @@ final class PHUICalendarMonthView extends AphrontView { $day->format('m').'/'. $day->format('d').'/'; - $cell_lists[] = array( + $day_id = $day->format('Ymd'); + + $cell_lists[$day_id] = array( + 'dayID' => $day_id, 'list' => $list, 'date' => $day, - 'uri' => $uri, + 'dayURI' => $uri, 'count' => count($all_day_events) + count($list_events), 'class' => $class, - ); + ); } $rows = array(); - $cell_lists_by_week = array_chunk($cell_lists, 7); - + $cell_lists_by_week = array_chunk($cell_lists, 7, true); foreach ($cell_lists_by_week as $week_of_cell_lists) { $cells = array(); - $max_count = $this->getMaxDailyEventsForWeek($week_of_cell_lists); + $action_map = array(); + foreach ($week_of_cell_lists as $day_id => $cell_list) { + $cells[] = $this->getEventListCell($cell_list); - foreach ($week_of_cell_lists as $cell_list) { - $cells[] = $this->getEventListCell($cell_list, $max_count); + $action_map[$day_id] = array( + 'dayURI' => $cell_list['dayURI'], + ); } - $rows[] = phutil_tag('tr', array(), $cells); + $rows[] = javelin_tag( + 'tr', + array( + 'sigil' => 'calendar-week calendar-week-body', + 'meta' => array( + 'actionMap' => $action_map, + ), + ), + $cells); $cells = array(); - foreach ($week_of_cell_lists as $cell_list) { + foreach ($week_of_cell_lists as $day_id => $cell_list) { $cells[] = $this->getDayNumberCell($cell_list); } - $rows[] = phutil_tag('tr', array(), $cells); + $rows[] = javelin_tag( + 'tr', + array( + 'sigil' => 'calendar-week calendar-week-foot', + ), + $cells); } $header = $this->getDayNamesHeader(); @@ -175,51 +195,53 @@ final class PHUICalendarMonthView extends AphrontView { return $max_count; } - private function getEventListCell($event_list, $max_count = 0) { + private function getEventListCell($event_list) { $list = $event_list['list']; $class = $event_list['class']; - $uri = $event_list['uri']; $count = $event_list['count']; $viewer_is_invited = $list->getIsViewerInvitedOnList(); - $event_count_badge = $this->getEventCountBadge($count, $viewer_is_invited); - $cell_day_secret_link = $this->getHiddenDayLink($uri, $max_count, 125); - $cell_data_div = phutil_tag( + $cell_content = phutil_tag( 'div', array( 'class' => 'phui-calendar-month-cell-div', ), array( - $cell_day_secret_link, $event_count_badge, $list, )); - return phutil_tag( + $cell_meta = array( + 'dayID' => $event_list['dayID'], + ); + + $classes = array(); + $classes[] = 'phui-calendar-month-event-list'; + $classes[] = $event_list['class']; + $classes = implode(' ', $classes); + + return javelin_tag( 'td', array( - 'class' => 'phui-calendar-month-event-list '.$class, + 'class' => $classes, + 'meta' => $cell_meta, ), - $cell_data_div); + $cell_content); } private function getDayNumberCell($event_list) { $class = $event_list['class']; $date = $event_list['date']; - $cell_day_secret_link = null; $week_number = null; if ($date) { - $uri = $event_list['uri']; - $cell_day_secret_link = $this->getHiddenDayLink($uri, 0, 25); - $cell_day = phutil_tag( 'a', array( 'class' => 'phui-calendar-date-number', - 'href' => $uri, + 'href' => $event_list['dayURI'], ), $date->format('j')); @@ -228,7 +250,7 @@ final class PHUICalendarMonthView extends AphrontView { 'a', array( 'class' => 'phui-calendar-week-number', - 'href' => $uri, + 'href' => $event_list['dayURI'], ), $date->format('W')); } @@ -256,14 +278,13 @@ final class PHUICalendarMonthView extends AphrontView { 'class' => 'phui-calendar-month-cell-div', ), array( - $cell_day_secret_link, $week_number, $cell_day, $today_slot, )); $classes = array(); - $classes[] = 'phui-calendar-date-number-container'; + $classes[] = 'phui-calendar-month-number'; if ($date) { if ($this->isDateInCurrentWeek($date)) { @@ -277,10 +298,15 @@ final class PHUICalendarMonthView extends AphrontView { } } - return phutil_tag( + $cell_meta = array( + 'dayID' => $event_list['dayID'], + ); + + return javelin_tag( 'td', array( 'class' => implode(' ', $classes), + 'meta' => $cell_meta, ), $cell_div); } @@ -320,21 +346,6 @@ final class PHUICalendarMonthView extends AphrontView { $event_count); } - private function getHiddenDayLink($uri, $count, $max_height) { - // approximately the height of the tallest cell - $height = 18 * $count + 5; - $height = ($height > $max_height) ? $height : $max_height; - $height_style = 'height: '.$height.'px'; - return phutil_tag( - 'a', - array( - 'class' => 'phui-calendar-month-secret-link', - 'style' => $height_style, - 'href' => $uri, - ), - null); - } - private function getDayNamesHeader() { list($week_start, $week_end) = $this->getWeekStartAndEnd(); diff --git a/webroot/rsrc/css/phui/calendar/phui-calendar-month.css b/webroot/rsrc/css/phui/calendar/phui-calendar-month.css index adaeabadd0..60166a4731 100644 --- a/webroot/rsrc/css/phui/calendar/phui-calendar-month.css +++ b/webroot/rsrc/css/phui/calendar/phui-calendar-month.css @@ -50,12 +50,6 @@ table.phui-calendar-view td { min-height: 32px; } -a.phui-calendar-month-secret-link { - position: absolute; - left: 0; - right: 0; -} - table.phui-calendar-view tr td:first-child { border-left-width: 0px; } @@ -113,7 +107,7 @@ table.phui-calendar-view a.phui-calendar-date-number { width: 12px; } -table.phui-calendar-view td.phui-calendar-date-number-container { +table.phui-calendar-view td.phui-calendar-month-number { font-weight: normal; color: {$lightgreytext}; border-width: 0 1px 0 1px; @@ -215,3 +209,13 @@ li.phui-calendar-viewer-invited.all-day { color: {$lightgreytext}; text-align: right; } + +td.phui-calendar-month-day, +td.phui-calendar-month-number { + cursor: pointer; +} + +.device-desktop td.phui-calendar-month-day.calendar-hover, +.device-desktop td.phui-calendar-month-number.calendar-hover { + background: {$lightblue}; +} diff --git a/webroot/rsrc/css/phui/calendar/phui-calendar.css b/webroot/rsrc/css/phui/calendar/phui-calendar.css index 637a479aa8..6c369b21df 100644 --- a/webroot/rsrc/css/phui/calendar/phui-calendar.css +++ b/webroot/rsrc/css/phui/calendar/phui-calendar.css @@ -2,6 +2,12 @@ * @provides phui-calendar-css */ +.phui-calendar-list { + /* When hovering over a day, this allows the hover color to peek through + the event name, but for event names to mostly remain readable. */ + background: rgba(255, 255, 255, 0.75); +} + .phui-calendar-list a { color: {$greytext}; } diff --git a/webroot/rsrc/js/application/calendar/behavior-month-view.js b/webroot/rsrc/js/application/calendar/behavior-month-view.js new file mode 100644 index 0000000000..914b3b929f --- /dev/null +++ b/webroot/rsrc/js/application/calendar/behavior-month-view.js @@ -0,0 +1,89 @@ +/** + * @provides javelin-behavior-calendar-month-view + */ +JX.behavior('calendar-month-view', function() { + + var hover_nodes = []; + + function get_info(e) { + var week_body = e.getNode('calendar-week-body'); + if (!week_body) { + week_body = e.getNode('calendar-week-foot').previousSibling; + } + + var week_foot = week_body.nextSibling; + var day_id = JX.Stratcom.getData(e.getNode('tag:td')).dayID; + + var day_body; + var day_foot; + var body_nodes = JX.DOM.scry(week_body, 'td'); + var foot_nodes = JX.DOM.scry(week_foot, 'td'); + for (var ii = 0; ii < body_nodes.length; ii++) { + if (JX.Stratcom.getData(body_nodes[ii]).dayID == day_id) { + day_body = body_nodes[ii]; + day_foot = foot_nodes[ii]; + break; + } + } + + return { + data: JX.Stratcom.getData(week_body), + dayID: day_id, + nodes: { + week: { + body: week_body, + foot: week_foot + }, + day: { + body: day_body, + foot: day_foot + } + } + }; + } + + function alter_hover(enable) { + for (var ii = 0; ii < hover_nodes.length; ii++) { + JX.DOM.alterClass(hover_nodes[ii], 'calendar-hover', enable); + } + } + + JX.enableDispatch(document.body, 'mouseover'); + JX.enableDispatch(document.body, 'mouseout'); + + JX.Stratcom.listen('mouseover', ['calendar-week', 'tag:td'], function(e) { + if (e.getNode('calendar-event-list')) { + alter_hover(false); + hover_nodes = []; + return; + } + + var info = get_info(e); + hover_nodes = [ + info.nodes.day.body, + info.nodes.day.foot + ]; + + alter_hover(true); + }); + + JX.Stratcom.listen('mouseout', ['calendar-week', 'tag:td'], function() { + alter_hover(false); + }); + + JX.Stratcom.listen('click', ['calendar-week', 'tag:td'], function(e) { + if (!e.isNormalClick()) { + return; + } + + // If this is a click in the event list or on a link, ignore it. This + // allows users to follow links to events and select text. + if (e.getNode('calendar-event-list') || e.getNode('tag:a')) { + return; + } + + var info = get_info(e); + JX.$U(info.data.actionMap[info.dayID].dayURI).go(); + }); + +});