Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3508991
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
16 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/web/calendar/calendar.css b/web/calendar/calendar.css
index 3d8ffe683..5f3b24da2 100644
--- a/web/calendar/calendar.css
+++ b/web/calendar/calendar.css
@@ -1,188 +1,192 @@
+.container {
+ display: flex;
+ flex-direction: row;
+}
+
.filtersButton {
color: var(--fg);
background-color: var(--filters-button-bg);
width: 40px;
height: 40px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin: 0 0px 9px 24px;
cursor: pointer;
position: absolute;
border: 1px solid var(--filters-button-border);
border-radius: 4px;
}
.filtersButton:hover {
background-color: var(--filters-button-hover-bg);
}
div.content {
height: 100%;
- position: relative;
overflow: auto;
}
nav.nav {
text-align: center;
margin: 18px auto 12px auto;
width: 350px;
color: #333333;
display: flex;
align-items: center;
}
nav.nav > a.monthLink {
color: var(--calendar-chevron);
}
nav.nav > .monthName {
display: inline-block;
width: 250px;
color: var(--fg);
}
table.calendar {
table-layout: fixed;
padding: 24px 28px 10px 28px;
width: 100%;
}
table.calendar > thead > tr > th {
padding-bottom: 6px;
color: #888888;
font-weight: normal;
text-transform: uppercase;
font-size: 14px;
}
textarea.entryText {
box-sizing: border-box;
font-size: 11px;
width: 100%;
background: none;
border: none;
outline: none;
resize: none;
overflow: auto;
overflow-y: hidden;
padding-right: 15px;
}
table.calendar td.day {
border: 1px solid transparent;
background: var(--calendar-day-bg);
position: relative;
vertical-align: top;
line-height: 0;
cursor: text;
height: 120px;
}
td.day > div.entryContainer {
overflow: auto;
height: 120px;
}
td.day > div.focusedEntryContainer {
height: 100px !important;
}
div.entryContainer > div.entryContainerSpacer {
height: 13px;
}
div.entry {
padding: 5px;
box-shadow: inset 0 0 0 2px white;
border-radius: 5px;
position: relative;
}
div.darkEntry {
color: white;
}
div.darkEntry textarea.entryText {
color: white;
}
div.darkEntry div.actionLinks > a {
color: lightgray;
}
div.darkEntry div.actionLinks > a:hover {
color: white;
}
span.entryLoading {
position: absolute;
top: 5px;
right: 5px;
width: 12px;
}
span.entryError {
position: absolute;
top: 11px;
right: 5px;
font-size: 16px;
width: 6px;
color: red !important;
}
div.actionLinks {
line-height: 90%;
margin-top: 3px;
font-size: 16px;
}
div.dayActionLinks {
padding-left: 3px;
margin-bottom: 1px;
height: 16px;
}
div.actionLinks a + a {
margin-left: 6px;
}
span.actionLinksText {
font-weight: 600;
font-size: 11px;
position: relative;
bottom: 1px;
padding-left: 5px;
}
span.rightActionLinks {
float: right;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
overflow: hidden;
}
div.actionLinks svg {
fill: var(--fg);
width: 10px;
height: 10px;
}
div.actionLinks svg.history {
position: relative;
top: 1px;
}
div.darkEntry div.actionLinks svg {
fill: var(--fg);
}
div.actionLinks a:hover svg {
fill: black;
}
div.darkEntry div.actionLinks a:hover svg {
fill: white;
}
div.focusedEntry {
z-index: 3 !important;
}
div.actionLinks a {
color: var(--fg);
}
div.actionLinks a:hover {
color: black;
}
td.day > h2 {
position: absolute;
bottom: 2px;
right: 3px;
color: var(--fg);
font-size: 32px;
line-height: 32px;
z-index: 1;
}
td.currentDay > h2 {
color: var(--calendar-day-selected-color);
}
div.clear {
clear: both;
}
diff --git a/web/calendar/calendar.react.js b/web/calendar/calendar.react.js
index b3d06fc0c..c6d07f1de 100644
--- a/web/calendar/calendar.react.js
+++ b/web/calendar/calendar.react.js
@@ -1,284 +1,282 @@
// @flow
import dateFormat from 'dateformat';
import invariant from 'invariant';
import * as React from 'react';
import {
updateCalendarQueryActionTypes,
updateCalendarQuery,
} from 'lib/actions/entry-actions.js';
import SWMansionIcon from 'lib/components/SWMansionIcon.react.js';
import { currentDaysToEntries } from 'lib/selectors/thread-selectors.js';
import { isLoggedIn } from 'lib/selectors/user-selectors.js';
import {
type EntryInfo,
type CalendarQuery,
type CalendarQueryUpdateResult,
type CalendarQueryUpdateStartingPayload,
} from 'lib/types/entry-types.js';
import {
type DispatchActionPromise,
useDispatchActionPromise,
useServerCall,
} from 'lib/utils/action-utils.js';
import {
getDate,
dateString,
startDateForYearAndMonth,
endDateForYearAndMonth,
} from 'lib/utils/date-utils.js';
import css from './calendar.css';
import Day from './day.react.js';
import FilterPanel from './filter-panel.react.js';
import { useSelector } from '../redux/redux-utils.js';
import {
yearAssertingSelector,
monthAssertingSelector,
webCalendarQuery,
} from '../selectors/nav-selectors.js';
import type { NavInfo } from '../types/nav-types.js';
import { canonicalURLFromReduxState } from '../url-utils.js';
type BaseProps = {
+url: string,
};
type Props = {
...BaseProps,
+year: number,
+month: number,
+daysToEntries: { +[dayString: string]: EntryInfo[] },
+navInfo: NavInfo,
+currentCalendarQuery: () => CalendarQuery,
+loggedIn: boolean,
+dispatchActionPromise: DispatchActionPromise,
+updateCalendarQuery: (
calendarQuery: CalendarQuery,
reduxAlreadyUpdated?: boolean,
) => Promise<CalendarQueryUpdateResult>,
};
type State = {
+filterPanelOpen: boolean,
};
class Calendar extends React.PureComponent<Props, State> {
state: State = {
filterPanelOpen: false,
};
getDate(
dayOfMonth: number,
monthInput: ?number = undefined,
yearInput: ?number = undefined,
) {
return getDate(
yearInput ? yearInput : this.props.year,
monthInput ? monthInput : this.props.month,
dayOfMonth,
);
}
prevMonthDates() {
const { year, month } = this.props;
const lastMonthDate = getDate(year, month - 1, 1);
const prevYear = lastMonthDate.getFullYear();
const prevMonth = lastMonthDate.getMonth() + 1;
return {
startDate: startDateForYearAndMonth(prevYear, prevMonth),
endDate: endDateForYearAndMonth(prevYear, prevMonth),
};
}
nextMonthDates() {
const { year, month } = this.props;
const nextMonthDate = getDate(year, month + 1, 1);
const nextYear = nextMonthDate.getFullYear();
const nextMonth = nextMonthDate.getMonth() + 1;
return {
startDate: startDateForYearAndMonth(nextYear, nextMonth),
endDate: endDateForYearAndMonth(nextYear, nextMonth),
};
}
render() {
const { year, month } = this.props;
const monthName = dateFormat(getDate(year, month, 1), 'mmmm');
const prevURL = canonicalURLFromReduxState(
{ ...this.props.navInfo, ...this.prevMonthDates() },
this.props.url,
this.props.loggedIn,
);
const nextURL = canonicalURLFromReduxState(
{ ...this.props.navInfo, ...this.nextMonthDates() },
this.props.url,
this.props.loggedIn,
);
const lastDayOfMonth = this.getDate(0, this.props.month + 1);
const totalDaysInMonth = lastDayOfMonth.getDate();
const firstDayToPrint = 1 - this.getDate(1).getDay();
const lastDayToPrint = totalDaysInMonth + 6 - lastDayOfMonth.getDay();
const rows = [];
let columns = [];
let week = 1;
let tabIndex = 1;
for (
let curDayOfMonth = firstDayToPrint;
curDayOfMonth <= lastDayToPrint;
curDayOfMonth++
) {
if (curDayOfMonth < 1 || curDayOfMonth > totalDaysInMonth) {
columns.push(<td key={curDayOfMonth} />);
} else {
const dayString = dateString(
this.props.year,
this.props.month,
curDayOfMonth,
);
const entries = this.props.daysToEntries[dayString];
invariant(
entries,
'the currentDaysToEntries selector should make sure all dayStrings ' +
`in the current range have entries, but ${dayString} did not`,
);
columns.push(
<Day
dayString={dayString}
entryInfos={entries}
key={curDayOfMonth}
startingTabIndex={tabIndex}
/>,
);
tabIndex += entries.length;
}
if (columns.length === 7) {
rows.push(<tr key={week++}>{columns}</tr>);
columns = [];
}
}
let filterPanel = null;
- let calendarContentStyle = null;
let filterButton = null;
if (this.state.filterPanelOpen) {
filterPanel = <FilterPanel toggleFilters={this.toggleFilters} />;
- calendarContentStyle = { marginLeft: '300px' };
} else {
filterButton = (
<a className={css.filtersButton} onClick={this.toggleFilters}>
<SWMansionIcon icon="filters-2" size={16} />
</a>
);
}
return (
- <div>
+ <div className={css.container}>
{filterPanel}
- <div className={css.content} style={calendarContentStyle}>
+ <div className={css.content}>
<div>
{filterButton}
<nav className={css.nav}>
<a
className={css.monthLink}
href={prevURL}
onClick={this.onClickPrevURL}
>
<SWMansionIcon icon="chevron-left" size={24} />
</a>
<h2 className={css.monthName}>
{' '}
{monthName} {year}{' '}
</h2>
<a
className={css.monthLink}
href={nextURL}
onClick={this.onClickNextURL}
>
<SWMansionIcon icon="chevron-right" size={24} />
</a>
</nav>
</div>
<table className={css.calendar}>
<thead>
<tr>
<th>Sunday</th>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
<th>Saturday</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
</div>
</div>
);
}
toggleFilters = (event: SyntheticEvent<HTMLAnchorElement>) => {
event.preventDefault();
this.setState({ filterPanelOpen: !this.state.filterPanelOpen });
};
onClickPrevURL = (event: SyntheticEvent<HTMLAnchorElement>) => {
event.preventDefault();
const currentCalendarQuery = this.props.currentCalendarQuery();
const newCalendarQuery = {
...currentCalendarQuery,
...this.prevMonthDates(),
};
this.props.dispatchActionPromise(
updateCalendarQueryActionTypes,
this.props.updateCalendarQuery(newCalendarQuery, true),
undefined,
({ calendarQuery: newCalendarQuery }: CalendarQueryUpdateStartingPayload),
);
};
onClickNextURL = (event: SyntheticEvent<HTMLAnchorElement>) => {
event.preventDefault();
const currentCalendarQuery = this.props.currentCalendarQuery();
const newCalendarQuery = {
...currentCalendarQuery,
...this.nextMonthDates(),
};
this.props.dispatchActionPromise(
updateCalendarQueryActionTypes,
this.props.updateCalendarQuery(newCalendarQuery, true),
undefined,
({ calendarQuery: newCalendarQuery }: CalendarQueryUpdateStartingPayload),
);
};
}
const ConnectedCalendar: React.ComponentType<BaseProps> = React.memo<BaseProps>(
function ConnectedCalendar(props) {
const year = useSelector(yearAssertingSelector);
const month = useSelector(monthAssertingSelector);
const daysToEntries = useSelector(currentDaysToEntries);
const navInfo = useSelector(state => state.navInfo);
const currentCalendarQuery = useSelector(webCalendarQuery);
const loggedIn = useSelector(isLoggedIn);
const callUpdateCalendarQuery = useServerCall(updateCalendarQuery);
const dispatchActionPromise = useDispatchActionPromise();
return (
<Calendar
{...props}
year={year}
month={month}
daysToEntries={daysToEntries}
navInfo={navInfo}
currentCalendarQuery={currentCalendarQuery}
loggedIn={loggedIn}
dispatchActionPromise={dispatchActionPromise}
updateCalendarQuery={callUpdateCalendarQuery}
/>
);
},
);
export default ConnectedCalendar;
diff --git a/web/calendar/filter-panel.css b/web/calendar/filter-panel.css
index ea74d3411..90a9a9fd0 100644
--- a/web/calendar/filter-panel.css
+++ b/web/calendar/filter-panel.css
@@ -1,181 +1,178 @@
div.container {
- position: fixed;
width: 300px;
- top: 126px;
- bottom: 0;
background-attachment: fixed;
display: flex;
flex-direction: column;
border-right: 1px solid var(--border);
background-color: var(--filter-panel-bg);
}
div.filters {
color: #dddddd;
overflow-y: auto;
height: 100%;
}
div.option {
margin: 5px 18px 5px 32px;
}
div.optionThread {
min-height: 20px;
position: relative;
display: flex;
align-items: center;
justify-content: flex-start;
}
div.optionThread input {
position: absolute;
left: 0;
top: 0;
width: calc(1em + 8px);
height: calc(1em + 8px);
z-index: 2;
opacity: 0;
cursor: pointer;
}
div.optionThread label {
font-weight: 600 !important;
font-size: 15px;
padding-left: 5px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
text-indent: 24px;
}
div.optionCheckbox {
display: block;
content: '';
width: calc(1em + 2px);
height: calc(1em + 2px);
border: 1px solid transparent;
z-index: 0;
position: absolute;
left: 0;
top: 0;
}
div.checkboxAfterOption {
-webkit-transform: scale(0.6);
-ms-transform: scale(0.6);
transform: scale(0.6);
}
a.only {
color: #888888;
padding-left: 5px;
font-size: 13px;
cursor: pointer;
display: none;
}
div.option:hover a.only {
display: inline;
}
a.only:hover {
text-decoration: underline;
color: #eeeeee;
}
a.settingsCog {
display: none;
margin-left: auto;
padding-left: 5px;
cursor: pointer;
color: #aaaaaa;
}
div.option:hover a.settingsCog {
display: inline;
}
a.settingsCog:hover {
color: #eeeeee;
}
div.optionDetails {
font-style: italic;
color: #888888;
font-size: 13px;
display: inline-block;
text-indent: 24px;
padding-left: 10px;
padding-top: 1px;
}
div.searchContainer {
padding: 8px 10px;
flex-direction: row;
display: flex;
}
.collapseButton {
color: var(--filter-panel-fg);
margin-left: 12px;
margin-right: 6px;
align-self: center;
display: flex;
}
.collapseButton:hover {
color: var(--fg);
}
div.search {
display: flex;
border-radius: 8px;
padding: 10px;
background-color: var(--text-input-bg);
align-items: center;
flex-grow: 1;
height: 20px;
}
svg.searchVector {
fill: var(--filter-panel-fg);
height: 20px;
width: 20px;
}
div.search > input {
color: white;
padding: 0;
border: none;
background-color: transparent;
font-weight: var(--normal);
font-size: var(--s-font-14);
flex-grow: 1;
margin-left: 8px;
}
div.search > input:focus {
outline: none;
}
svg.clearQuery {
font-size: 15px;
padding-bottom: 1px;
padding-right: 2px;
color: #cccccc;
}
svg.clearQuery:hover {
font-size: 15px;
padding-bottom: 1px;
padding-right: 2px;
color: white;
}
div.noResults {
font-style: italic;
text-align: center;
font-size: 15px;
color: #888888;
}
a.collapse {
margin-left: auto;
padding-left: 5px;
cursor: pointer;
color: #aaaaaa;
}
div.category {
margin: 5px 18px 8px 18px;
}
div.extras {
background-color: rgba(0, 0, 0, 0.89);
color: #dddddd;
padding: 8px 20px;
font-size: 14px;
display: flex;
}
div.extras > label {
display: flex;
align-items: center;
}
div.extras > label > span {
margin-left: 8px;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Dec 23, 12:26 AM (3 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2690048
Default Alt Text
(16 KB)
Attached To
Mode
rCOMM Comm
Attached
Detach File
Event Timeline
Log In to Comment