diff options
8 files changed, 110 insertions, 61 deletions
diff --git a/Timeline/ClientApp/.vscode/extensions.json b/Timeline/ClientApp/.vscode/extensions.json index 7d55bb42..be640996 100644 --- a/Timeline/ClientApp/.vscode/extensions.json +++ b/Timeline/ClientApp/.vscode/extensions.json @@ -3,6 +3,7 @@ "dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"arcanis.vscode-zipfs",
- "syler.sass-indented"
+ "syler.sass-indented",
+ "editorconfig.editorconfig"
]
}
diff --git a/Timeline/ClientApp/src/app/views/timeline-common/CollapseCard.tsx b/Timeline/ClientApp/src/app/views/timeline-common/CollapseCard.tsx new file mode 100644 index 00000000..1c233058 --- /dev/null +++ b/Timeline/ClientApp/src/app/views/timeline-common/CollapseCard.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import clsx from "clsx"; + +import SyncStatusBadge, { + TimelineSyncStatus, +} from "../timeline-common/SyncStatusBadge"; +import CollapseButton from "../timeline-common/CollapseButton"; + +const CollapseCard: React.FC<{ + className?: string; + syncStatus: TimelineSyncStatus; + toggleCollapse: () => void; + visible: boolean; +}> = ({ className, syncStatus, toggleCollapse, visible }) => { + return ( + <div + style={{ visibility: visible ? "visible" : "hidden" }} + className={clsx("cru-card p-2", className)} + > + <div className="d-flex align-items-center"> + <SyncStatusBadge status={syncStatus} className="mr-2" /> + <CollapseButton collapse onClick={toggleCollapse} /> + </div> + </div> + ); +}; + +export default CollapseCard; diff --git a/Timeline/ClientApp/src/app/views/timeline-common/InfoCardTemplate.tsx b/Timeline/ClientApp/src/app/views/timeline-common/InfoCardTemplate.tsx index a8de20aa..a5166aa4 100644 --- a/Timeline/ClientApp/src/app/views/timeline-common/InfoCardTemplate.tsx +++ b/Timeline/ClientApp/src/app/views/timeline-common/InfoCardTemplate.tsx @@ -8,17 +8,17 @@ import CollapseButton from "../timeline-common/CollapseButton"; const InfoCardTemplate: React.FC< Pick< TimelineCardComponentProps<"">, - "collapse" | "toggleCollapse" | "syncStatus" | "className" + "toggleCollapse" | "syncStatus" | "className" > & { children: React.ReactElement[] } -> = ({ collapse, toggleCollapse, syncStatus, className, children }) => { +> = ({ toggleCollapse, syncStatus, className, children }) => { return ( <div className={clsx("cru-card p-2 clearfix", className)}> <div className="float-right d-flex align-items-center"> <SyncStatusBadge status={syncStatus} className="mr-2" /> - <CollapseButton collapse={collapse} onClick={toggleCollapse} /> + <CollapseButton collapse={false} onClick={toggleCollapse} /> </div> - <div style={{ display: collapse ? "none" : "block" }}>{children}</div> + <div>{children}</div> </div> ); }; diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx index 58fd024b..ef13fa50 100644 --- a/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx +++ b/Timeline/ClientApp/src/app/views/timeline-common/TimelinePageTemplateUI.tsx @@ -16,6 +16,8 @@ import Timeline, { TimelinePostInfoEx, TimelineDeleteCallback, } from "./Timeline"; +import TimelineTop from "./TimelineTop"; +import CollapseCard from "./CollapseCard"; import TimelinePostEdit, { TimelinePostSendCallback } from "./TimelinePostEdit"; import { TimelineSyncStatus } from "./SyncStatusBadge"; import clsx from "clsx"; @@ -25,7 +27,6 @@ export interface TimelineCardComponentProps<TManageItems> { onManage?: (item: TManageItems | "property") => void; onMember: () => void; className?: string; - collapse: boolean; syncStatus: TimelineSyncStatus; toggleCollapse: () => void; } @@ -128,15 +129,26 @@ export default function TimelinePageTemplateUI<TManageItems>( const cardCollapseLocalStorageKey = timeline != null ? genCardCollapseLocalStorageKey(timeline.uniqueId) : null; - const [infoCardCollapse, setInfoCardCollapse] = React.useState<boolean>(true); + const [cardCollapse, setCardCollapse] = React.useState<boolean>(true); React.useEffect(() => { if (cardCollapseLocalStorageKey != null) { const savedCollapse = window.localStorage.getItem(cardCollapseLocalStorageKey) === "true"; - setInfoCardCollapse(savedCollapse); + setCardCollapse(savedCollapse); } }, [cardCollapseLocalStorageKey]); + const toggleCardCollapse = (): void => { + const newState = !cardCollapse; + setCardCollapse(newState); + if (timeline != null) { + window.localStorage.setItem( + genCardCollapseLocalStorageKey(timeline.uniqueId), + newState.toString() + ); + } + }; + let body: React.ReactElement; if (props.error != null) { @@ -207,30 +219,31 @@ export default function TimelinePageTemplateUI<TManageItems>( body = ( <> - <div - className={clsx( - "timeline-template-info-card", - infoCardCollapse && "my-collapse" - )} - > - <CardComponent - timeline={timeline} - onManage={props.onManage} - onMember={props.onMember} - syncStatus={syncStatus} - collapse={infoCardCollapse} - toggleCollapse={() => { - const newState = !infoCardCollapse; - setInfoCardCollapse(newState); - if (timeline != null) { - window.localStorage.setItem( - genCardCollapseLocalStorageKey(timeline.uniqueId), - newState.toString() - ); - } - }} - /> - </div> + <TimelineTop> + <div + className={clsx( + "timeline-template-card-container", + cardCollapse || "my-expand" + )} + > + <CollapseCard + visible={cardCollapse} + syncStatus={syncStatus} + toggleCollapse={toggleCardCollapse} + /> + <CardComponent + className={clsx( + "timeline-template-card", + cardCollapse && "d-none" + )} + timeline={timeline} + onManage={props.onManage} + onMember={props.onMember} + syncStatus={syncStatus} + toggleCollapse={toggleCardCollapse} + /> + </div> + </TimelineTop> {timelineBody} </> ); diff --git a/Timeline/ClientApp/src/app/views/timeline-common/TimelineTop.tsx b/Timeline/ClientApp/src/app/views/timeline-common/TimelineTop.tsx new file mode 100644 index 00000000..a98d3687 --- /dev/null +++ b/Timeline/ClientApp/src/app/views/timeline-common/TimelineTop.tsx @@ -0,0 +1,18 @@ +import React from "react"; + +export interface TimelineTopProps { + children: React.ReactElement; +} + +const TimelineTop: React.FC<TimelineTopProps> = ({ children }) => { + return ( + <div className="timeline-top"> + <div className="timeline-line-area"> + <div className="timeline-line-segment"></div> + </div> + {children} + </div> + ); +}; + +export default TimelineTop; 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 79df9249..aa3544fe 100644 --- a/Timeline/ClientApp/src/app/views/timeline-common/timeline-common.sass +++ b/Timeline/ClientApp/src/app/views/timeline-common/timeline-common.sass @@ -70,6 +70,13 @@ $timeline-line-color-current: #36c2e6 animation: 1s infinite alternate animation-name: timeline-line-node-noncurrent +.timeline-top + display: flex + justify-content: space-between + + .timeline-line-segment + flex: 1 1 auto + .current .timeline-line &-segment @@ -84,7 +91,7 @@ $timeline-line-color-current: #36c2e6 animation-name: timeline-line-node-current .timeline-content-area - padding-top: 18px + padding: 10px 0 flex-grow: 1 .timeline-item-delete-button @@ -124,17 +131,15 @@ $timeline-line-color-current: #36c2e6 vertical-align: middle margin-right: 0.6em -.timeline-template-info-card +.timeline-template-card-container position: sticky z-index: 1 top: 56px - padding: 0.5em - - @include media-breakpoint-down(sm) - padding-bottom: 0 + margin: 0.5em - &.my-collapse - float: right - - @include media-breakpoint-up(sm) - float: right +.timeline-template-card + width: 360px + max-width: calc(100vw - 1em) + position: absolute + top: 0 + right: 0 diff --git a/Timeline/ClientApp/src/app/views/timeline/TimelineInfoCard.tsx b/Timeline/ClientApp/src/app/views/timeline/TimelineInfoCard.tsx index d764a275..6966ab41 100644 --- a/Timeline/ClientApp/src/app/views/timeline/TimelineInfoCard.tsx +++ b/Timeline/ClientApp/src/app/views/timeline/TimelineInfoCard.tsx @@ -16,14 +16,7 @@ export type TimelineInfoCardProps = TimelineCardComponentProps< >; const TimelineInfoCard: React.FC<TimelineInfoCardProps> = (props) => { - const { - timeline, - onMember, - onManage, - collapse, - syncStatus, - toggleCollapse, - } = props; + const { timeline, onMember, onManage, syncStatus, toggleCollapse } = props; const { t } = useTranslation(); @@ -33,7 +26,6 @@ const TimelineInfoCard: React.FC<TimelineInfoCardProps> = (props) => { <InfoCardTemplate className={props.className} syncStatus={syncStatus} - collapse={collapse} toggleCollapse={toggleCollapse} > <h3 className="text-primary mx-3 d-inline-block align-middle"> diff --git a/Timeline/ClientApp/src/app/views/user/UserInfoCard.tsx b/Timeline/ClientApp/src/app/views/user/UserInfoCard.tsx index 4cf11e62..bad2a9e1 100644 --- a/Timeline/ClientApp/src/app/views/user/UserInfoCard.tsx +++ b/Timeline/ClientApp/src/app/views/user/UserInfoCard.tsx @@ -16,14 +16,7 @@ export type UserInfoCardProps = TimelineCardComponentProps< >; const UserInfoCard: React.FC<UserInfoCardProps> = (props) => { - const { - timeline, - onMember, - onManage, - syncStatus, - collapse, - toggleCollapse, - } = props; + const { timeline, onMember, onManage, syncStatus, toggleCollapse } = props; const { t } = useTranslation(); const avatar = useAvatar(timeline?.owner?.username); @@ -32,7 +25,6 @@ const UserInfoCard: React.FC<UserInfoCardProps> = (props) => { <InfoCardTemplate className={props.className} syncStatus={syncStatus} - collapse={collapse} toggleCollapse={toggleCollapse} > <div> |