Page MenuHomePhabricator

No OneTemporary

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

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)

Event Timeline