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, }; type State = { +filterPanelOpen: boolean, }; class Calendar extends React.PureComponent { 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(); } 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( , ); tabIndex += entries.length; } if (columns.length === 7) { rows.push({columns}); columns = []; } } let filterPanel = null; - let calendarContentStyle = null; let filterButton = null; if (this.state.filterPanelOpen) { filterPanel = ; - calendarContentStyle = { marginLeft: '300px' }; } else { filterButton = ( ); } return ( -
+
{filterPanel} -
+
{filterButton}
{rows}
Sunday Monday Tuesday Wednesday Thursday Friday Saturday
); } toggleFilters = (event: SyntheticEvent) => { event.preventDefault(); this.setState({ filterPanelOpen: !this.state.filterPanelOpen }); }; onClickPrevURL = (event: SyntheticEvent) => { 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) => { 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 = React.memo( 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 ( ); }, ); 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; }