From 01446b3c8306112cd965eeaaa40a0ac573cc374e Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 3 Sep 2020 21:10:58 +0800 Subject: ... --- .../app/views/timeline-common/CollapseButton.tsx | 23 ++++ .../app/views/timeline-common/SyncStatusBadge.tsx | 58 ++++++++++ .../src/app/views/timeline-common/Timeline.tsx | 5 +- .../src/app/views/timeline-common/TimelineItem.tsx | 58 +++++----- .../timeline-common/TimelinePageTemplateUI.tsx | 128 ++++++--------------- .../app/views/timeline-common/timeline-common.sass | 23 ++-- 6 files changed, 155 insertions(+), 140 deletions(-) create mode 100644 Timeline/ClientApp/src/app/views/timeline-common/CollapseButton.tsx create mode 100644 Timeline/ClientApp/src/app/views/timeline-common/SyncStatusBadge.tsx (limited to 'Timeline/ClientApp/src/app/views/timeline-common') diff --git a/Timeline/ClientApp/src/app/views/timeline-common/CollapseButton.tsx b/Timeline/ClientApp/src/app/views/timeline-common/CollapseButton.tsx new file mode 100644 index 00000000..3c52150f --- /dev/null +++ b/Timeline/ClientApp/src/app/views/timeline-common/CollapseButton.tsx @@ -0,0 +1,23 @@ +import React from "react"; +import clsx from "clsx"; +import Svg from "react-inlinesvg"; +import arrowsAngleContractIcon from "bootstrap-icons/icons/arrows-angle-contract.svg"; +import arrowsAngleExpandIcon from "bootstrap-icons/icons/arrows-angle-expand.svg"; + +const CollapseButton: React.FC<{ + collapse: boolean; + onClick: () => void; + className?: string; + style?: React.CSSProperties; +}> = ({ collapse, onClick, className, style }) => { + return ( + + ); +}; + +export default CollapseButton; diff --git a/Timeline/ClientApp/src/app/views/timeline-common/SyncStatusBadge.tsx b/Timeline/ClientApp/src/app/views/timeline-common/SyncStatusBadge.tsx new file mode 100644 index 00000000..e67cfb43 --- /dev/null +++ b/Timeline/ClientApp/src/app/views/timeline-common/SyncStatusBadge.tsx @@ -0,0 +1,58 @@ +import React from "react"; +import clsx from "clsx"; +import { useTranslation } from "react-i18next"; + +import { UiLogicError } from "@/common"; + +export type TimelineSyncStatus = "syncing" | "synced" | "offline"; + +const SyncStatusBadge: React.FC<{ + status: TimelineSyncStatus; + style?: React.CSSProperties; + className?: string; +}> = ({ status, style, className }) => { + const { t } = useTranslation(); + + return ( +
+ {(() => { + switch (status) { + case "syncing": { + return ( + <> + + + {t("timeline.postSyncState.syncing")} + + + ); + } + case "synced": { + return ( + <> + + + {t("timeline.postSyncState.synced")} + + + ); + } + case "offline": { + return ( + <> + + + {t("timeline.postSyncState.offline")} + + + ); + } + default: + throw new UiLogicError("Unknown sync state."); + } + })()} +
+ ); +}; + +export default SyncStatusBadge; diff --git a/Timeline/ClientApp/src/app/views/timeline-common/Timeline.tsx b/Timeline/ClientApp/src/app/views/timeline-common/Timeline.tsx index 1ad62a51..1cb15d8e 100644 --- a/Timeline/ClientApp/src/app/views/timeline-common/Timeline.tsx +++ b/Timeline/ClientApp/src/app/views/timeline-common/Timeline.tsx @@ -51,10 +51,7 @@ const Timeline: React.FC = (props) => { }, [posts, onDelete]); return ( -
+
{(() => { const length = posts.length; diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelineItem.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelineItem.tsx index ce371015..09d74d3c 100644 --- a/Timeline/ClientApp/src/app/views/timeline-common/TimelineItem.tsx +++ b/Timeline/ClientApp/src/app/views/timeline-common/TimelineItem.tsx @@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next"; import Svg from "react-inlinesvg"; import chevronDownIcon from "bootstrap-icons/icons/chevron-down.svg"; import trashIcon from "bootstrap-icons/icons/trash.svg"; -import { Row, Col, Modal, Button } from "react-bootstrap"; +import { Modal, Button } from "react-bootstrap"; import { useAvatar } from "@/services/user"; import { TimelinePostInfo } from "@/services/timeline"; @@ -74,51 +74,45 @@ const TimelineItem: React.FC = (props) => { ); return ( - - +
{current &&
} - - - -
- - - {props.post.time.toLocaleString(i18n.languages)} - - - {props.post.author.nickname} - - -
+
+
+
+ + + {props.post.time.toLocaleString(i18n.languages)} + + {props.post.author.nickname} + {more != null ? ( -
- { - more.toggle(); - e.stopPropagation(); - }} - /> -
+ { + more.toggle(); + e.stopPropagation(); + }} + /> ) : null} - -
+
+
= (props) => { } })()}
- +
{more != null && more.isOpen ? ( <>
= (props) => { ) : null} ) : null} - +
); }; diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx index 4296a5ce..c2d4aeaa 100644 --- a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx +++ b/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx @@ -1,11 +1,7 @@ -import React, { CSSProperties } from "react"; -import clsx from "clsx"; +import React from "react"; import { useTranslation } from "react-i18next"; import { fromEvent } from "rxjs"; -import Svg from "react-inlinesvg"; -import { Spinner, Collapse } from "react-bootstrap"; -import arrowsAngleContractIcon from "bootstrap-icons/icons/arrows-angle-contract.svg"; -import arrowsAngleExpandIcon from "bootstrap-icons/icons/arrows-angle-expand.svg"; +import { Spinner } from "react-bootstrap"; import { getAlertHost } from "@/services/alert"; import { useEventEmiiter, UiLogicError } from "@/common"; @@ -21,63 +17,16 @@ import Timeline, { TimelineDeleteCallback, } from "./Timeline"; import TimelinePostEdit, { TimelinePostSendCallback } from "./TimelinePostEdit"; - -type TimelinePostSyncState = "syncing" | "synced" | "offline"; - -const TimelinePostSyncStateBadge: React.FC<{ - state: TimelinePostSyncState; - style?: CSSProperties; - className?: string; -}> = ({ state, style, className }) => { - const { t } = useTranslation(); - - return ( -
- {(() => { - switch (state) { - case "syncing": { - return ( - <> - - - {t("timeline.postSyncState.syncing")} - - - ); - } - case "synced": { - return ( - <> - - - {t("timeline.postSyncState.synced")} - - - ); - } - case "offline": { - return ( - <> - - - {t("timeline.postSyncState.offline")} - - - ); - } - default: - throw new UiLogicError("Unknown sync state."); - } - })()} -
- ); -}; +import { TimelineSyncStatus } from "./SyncStatusBadge"; export interface TimelineCardComponentProps { timeline: TimelineInfo; onManage?: (item: TManageItems | "property") => void; onMember: () => void; className?: string; + collapse: boolean; + syncStatus: TimelineSyncStatus; + toggleCollapse: () => void; } export interface TimelinePageTemplateUIProps { @@ -216,22 +165,13 @@ export default function TimelinePageTemplateUI( }) ); - const syncState: TimelinePostSyncState = postListState.syncing - ? "syncing" - : postListState.type === "synced" - ? "synced" - : "offline"; - timelineBody = ( -
- - -
+ ); if (props.onPost != null) { timelineBody = ( @@ -255,37 +195,35 @@ export default function TimelinePageTemplateUI(
); } + const { CardComponent } = props; + const syncStatus: TimelineSyncStatus = + postListState == null || postListState.syncing + ? "syncing" + : postListState.type === "synced" + ? "synced" + : "offline"; body = ( <> -
- { - const newState = !infoCardCollapse; - setInfoCardCollapse(newState); + { + const newState = !infoCardCollapse; + setInfoCardCollapse(newState); + if (timeline != null) { window.localStorage.setItem( genCardCollapseLocalStorageKey(timeline.uniqueId), newState.toString() ); - }} - className="float-right m-1 info-card-collapse-button text-primary icon-button" - /> - - - -
- + } + }} + /> {timelineBody} ); diff --git a/Timeline/ClientApp/src/app/views/timeline-common/timeline-common.sass b/Timeline/ClientApp/src/app/views/timeline-common/timeline-common.sass index a7b9af7b..ad024c78 100644 --- a/Timeline/ClientApp/src/app/views/timeline-common/timeline-common.sass +++ b/Timeline/ClientApp/src/app/views/timeline-common/timeline-common.sass @@ -1,11 +1,12 @@ @use 'sass:color' .timeline - display: flex - flex-direction: column z-index: 0 position: relative + &-item + display: flex + @keyframes timeline-enter-animation-mask-animation to height: 0 @@ -96,8 +97,9 @@ $timeline-line-color-current: #36c2e6 &-node animation-name: timeline-line-node-current -.timeline-pt-start +.timeline-content-area padding-top: 18px + flex-grow: 1 .timeline-item-delete-button position: absolute @@ -123,10 +125,6 @@ $timeline-line-color-current: #36c2e6 transition: height 0.5s .timeline-sync-state-badge - position: fixed - top: 0 - right: 0 - z-index: 1 font-size: 0.8em padding: 3px 8px border-radius: 5px @@ -140,7 +138,14 @@ $timeline-line-color-current: #36c2e6 vertical-align: middle margin-right: 0.6em -.info-card-container +.timeline-info-card position: sticky - top: 56px z-index: 1 + top: 56px + margin: 0.5em + + @include media-breakpoint-down(sm) + margin-bottom: 0 + + @include media-breakpoint-up(sm) + float: right -- cgit v1.2.3