diff options
author | crupest <crupest@outlook.com> | 2023-08-31 23:56:13 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2023-08-31 23:56:13 +0800 |
commit | 6664fb3506b1ea4af712fa849bd7c761a06c9843 (patch) | |
tree | 1a15efb73c5608d336587390aadea9a9a210cc25 /FrontEnd/src | |
parent | a1f69d978426c6a4cb7e8f3116e087553dbbffd5 (diff) | |
download | timeline-6664fb3506b1ea4af712fa849bd7c761a06c9843.tar.gz timeline-6664fb3506b1ea4af712fa849bd7c761a06c9843.tar.bz2 timeline-6664fb3506b1ea4af712fa849bd7c761a06c9843.zip |
...
Diffstat (limited to 'FrontEnd/src')
-rw-r--r-- | FrontEnd/src/components/dialog/Dialog.tsx | 4 | ||||
-rw-r--r-- | FrontEnd/src/components/dialog/FullPageDialog.css | 28 | ||||
-rw-r--r-- | FrontEnd/src/components/dialog/FullPageDialog.tsx | 80 | ||||
-rw-r--r-- | FrontEnd/src/components/hooks/responsive.ts | 4 | ||||
-rw-r--r-- | FrontEnd/src/pages/timeline/CollapseButton.tsx | 25 | ||||
-rw-r--r-- | FrontEnd/src/pages/timeline/Timeline.tsx | 4 | ||||
-rw-r--r-- | FrontEnd/src/pages/timeline/TimelineInfoCard.css (renamed from FrontEnd/src/pages/timeline/TimelineCard.css) | 0 | ||||
-rw-r--r-- | FrontEnd/src/pages/timeline/TimelineInfoCard.tsx (renamed from FrontEnd/src/pages/timeline/TimelineCard.tsx) | 97 |
8 files changed, 121 insertions, 121 deletions
diff --git a/FrontEnd/src/components/dialog/Dialog.tsx b/FrontEnd/src/components/dialog/Dialog.tsx index 85e8ca46..043a8eec 100644 --- a/FrontEnd/src/components/dialog/Dialog.tsx +++ b/FrontEnd/src/components/dialog/Dialog.tsx @@ -2,7 +2,7 @@ import { ReactNode, useRef } from "react"; import ReactDOM from "react-dom"; import classNames from "classnames"; -import { ThemeColor } from "../common"; +import { ThemeColor, UiLogicError } from "../common"; import { useCloseDialog } from "./DialogProvider"; @@ -10,7 +10,7 @@ import "./Dialog.css"; const optionalPortalElement = document.getElementById("portal"); if (optionalPortalElement == null) { - throw new Error("Portal element not found"); + throw new UiLogicError(); } const portalElement = optionalPortalElement; diff --git a/FrontEnd/src/components/dialog/FullPageDialog.css b/FrontEnd/src/components/dialog/FullPageDialog.css index 2f1fc636..ce07c6ac 100644 --- a/FrontEnd/src/components/dialog/FullPageDialog.css +++ b/FrontEnd/src/components/dialog/FullPageDialog.css @@ -1,44 +1,30 @@ -.cru-full-page {
+.cru-dialog-full-page {
position: fixed;
z-index: 1030;
left: 0;
top: 0;
right: 0;
bottom: 0;
- background-color: white;
+ background-color: var(--cru-background-color);
padding-top: 56px;
}
-.cru-full-page-top-bar {
+.cru-dialog-full-page-top-bar {
height: 56px;
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 1;
- background-color: var(--cru-primary-color);
+ background-color: var(--cru-theme-color);
display: flex;
align-items: center;
}
-.cru-full-page-content-container {
+.cru-dialog-full-page-content-container {
overflow: scroll;
}
-.cru-full-page-back-button {
- color: var(--cru-primary-t-color);
-}
-
-.cru-full-page-enter {
- transform: translate(100%, 0);
-}
-
-.cru-full-page-enter-active {
- transform: none;
- transition: transform 0.3s;
-}
-
-.cru-full-page-exit-active {
- transition: transform 0.3s;
- transform: translate(100%, 0);
+.cru-dialog-full-page-back-button {
+ margin-left: 0.5em;
}
diff --git a/FrontEnd/src/components/dialog/FullPageDialog.tsx b/FrontEnd/src/components/dialog/FullPageDialog.tsx index cba57e21..d18bcf73 100644 --- a/FrontEnd/src/components/dialog/FullPageDialog.tsx +++ b/FrontEnd/src/components/dialog/FullPageDialog.tsx @@ -1,53 +1,51 @@ -import * as React from "react"; +import { ReactNode } from "react"; import { createPortal } from "react-dom"; -import classnames from "classnames"; -import { CSSTransition } from "react-transition-group"; +import classNames from "classnames"; + +import { ThemeColor, UiLogicError } from "../common"; +import { IconButton } from "../button"; + +import { useCloseDialog } from "./DialogProvider"; import "./FullPageDialog.css"; -import IconButton from "../button/IconButton"; -export interface FullPageDialogProps { - show: boolean; - onBack: () => void; +const optionalPortalElement = document.getElementById("portal"); +if (optionalPortalElement == null) { + throw new UiLogicError(); +} +const portalElement = optionalPortalElement; + +interface FullPageDialogProps { + color?: ThemeColor; contentContainerClassName?: string; - children: React.ReactNode; + children: ReactNode; } -const FullPageDialog: React.FC<FullPageDialogProps> = ({ - show, - onBack, +export default function FullPageDialog({ + color, children, contentContainerClassName, -}) => { +}: FullPageDialogProps) { + const closeDialog = useCloseDialog(); + return createPortal( - <CSSTransition - mountOnEnter - unmountOnExit - in={show} - timeout={300} - classNames="cru-full-page" - > - <div className="cru-full-page"> - <div className="cru-full-page-top-bar"> - <IconButton - icon="arrow-left" - className="ms-3 cru-full-page-back-button" - onClick={onBack} - /> - </div> - <div - className={classnames( - "cru-full-page-content-container", - contentContainerClassName, - )} - > - {children} - </div> + <div className={`cru-dialog-full-page cru-theme-${color ?? "primary"}`}> + <div className="cru-dialog-full-page-top-bar"> + <IconButton + icon="arrow-left" + className="cru-dialog-full-page-back-button" + onClick={closeDialog} + /> + </div> + <div + className={classNames( + "cru-dialog-full-page-content-container", + contentContainerClassName, + )} + > + {children} </div> - </CSSTransition>, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - document.getElementById("portal")!, + </div>, + portalElement, ); -}; - -export default FullPageDialog; +} diff --git a/FrontEnd/src/components/hooks/responsive.ts b/FrontEnd/src/components/hooks/responsive.ts index 6bcce96c..42c134ef 100644 --- a/FrontEnd/src/components/hooks/responsive.ts +++ b/FrontEnd/src/components/hooks/responsive.ts @@ -2,6 +2,6 @@ import { useMediaQuery } from "react-responsive"; import { breakpoints } from "../breakpoints"; -export function useMobile(): boolean { - return useMediaQuery({ maxWidth: breakpoints.sm }); +export function useMobile(onChange?: (mobile: boolean) => void): boolean { + return useMediaQuery({ maxWidth: breakpoints.sm }, undefined, onChange); } diff --git a/FrontEnd/src/pages/timeline/CollapseButton.tsx b/FrontEnd/src/pages/timeline/CollapseButton.tsx deleted file mode 100644 index 1c4fa2ba..00000000 --- a/FrontEnd/src/pages/timeline/CollapseButton.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { CSSProperties } from "react"; - -import IconButton from "~src/components/button/IconButton"; - -export default function CollapseButton({ - collapse, - onClick, - className, - style, -}: { - collapse: boolean; - onClick: () => void; - className?: string; - style?: CSSProperties; -}) { - return ( - <IconButton - color="primary" - icon={collapse ? "arrows-angle-expand" : "arrows-angle-contract"} - onClick={onClick} - className={className} - style={style} - /> - ); -} diff --git a/FrontEnd/src/pages/timeline/Timeline.tsx b/FrontEnd/src/pages/timeline/Timeline.tsx index caf4f502..4fe5c521 100644 --- a/FrontEnd/src/pages/timeline/Timeline.tsx +++ b/FrontEnd/src/pages/timeline/Timeline.tsx @@ -19,7 +19,7 @@ import { useScrollToBottom } from "~src/components/hooks"; import TimelinePostList from "./TimelinePostList"; import TimelinePostEdit from "./TimelinePostCreateView"; -import TimelineCard from "./TimelineCard"; +import TimelineInfoCard from "./TimelineInfoCard"; import "./Timeline.css"; @@ -159,7 +159,7 @@ export function Timeline(props: TimelineProps) { return ( <div className="timeline-container"> {timeline && ( - <TimelineCard + <TimelineInfoCard timeline={timeline} connectionStatus={signalrState} onReload={updateTimeline} diff --git a/FrontEnd/src/pages/timeline/TimelineCard.css b/FrontEnd/src/pages/timeline/TimelineInfoCard.css index 29e59b62..29e59b62 100644 --- a/FrontEnd/src/pages/timeline/TimelineCard.css +++ b/FrontEnd/src/pages/timeline/TimelineInfoCard.css diff --git a/FrontEnd/src/pages/timeline/TimelineCard.tsx b/FrontEnd/src/pages/timeline/TimelineInfoCard.tsx index 1e0e9b75..b1310be9 100644 --- a/FrontEnd/src/pages/timeline/TimelineCard.tsx +++ b/FrontEnd/src/pages/timeline/TimelineInfoCard.tsx @@ -8,38 +8,55 @@ import { getHttpBookmarkClient } from "~src/http/bookmark"; import { pushAlert } from "~src/components/alert"; import { useMobile } from "~src/components/hooks"; -import { Dialog, DialogProvider, useDialog } from "~src/components/dialog"; +import { IconButton } from "~src/components/button"; +import { + Dialog, + FullPageDialog, + DialogProvider, + useDialog, +} from "~src/components/dialog"; import UserAvatar from "~src/components/user/UserAvatar"; import PopupMenu from "~src/components/menu/PopupMenu"; -import FullPageDialog from "~src/components/dialog/FullPageDialog"; import Card from "~src/components/Card"; + import TimelineDeleteDialog from "./TimelineDeleteDialog"; import ConnectionStatusBadge from "./ConnectionStatusBadge"; -import CollapseButton from "./CollapseButton"; import TimelineMember from "./TimelineMember"; import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog"; -import IconButton from "~src/components/button/IconButton"; -import "./TimelineCard.css"; +import "./TimelineInfoCard.css"; + +function CollapseButton({ + collapse, + onClick, + className, +}: { + collapse: boolean; + onClick: () => void; + className?: string; +}) { + return ( + <IconButton + color="primary" + icon={collapse ? "info-circle" : "x-circle"} + onClick={onClick} + className={className} + /> + ); +} -interface TimelinePageCardProps { +interface TimelineInfoCardProps { timeline: HttpTimelineInfo; connectionStatus: HubConnectionState; onReload: () => void; } -export default function TimelineCard(props: TimelinePageCardProps) { - const { timeline, connectionStatus, onReload } = props; - +function TimelineInfoContent({ + timeline, + onReload, +}: Omit<TimelineInfoCardProps, "connectionStatus">) { const user = useUser(); - const [collapse, setCollapse] = useState(true); - const toggleCollapse = (): void => { - setCollapse((o) => !o); - }; - - const isMobile = useMobile(); - const { controller, createDialogSwitch } = useDialog({ member: ( <Dialog> @@ -52,7 +69,7 @@ export default function TimelineCard(props: TimelinePageCardProps) { delete: <TimelineDeleteDialog timeline={timeline} />, }); - const content = ( + return ( <div> <h3 className="timeline-card-title"> {timeline.title} @@ -127,8 +144,31 @@ export default function TimelineCard(props: TimelinePageCardProps) { </PopupMenu> )} </div> + <DialogProvider controller={controller} /> </div> ); +} + +export default function TimelineInfoCard(props: TimelineInfoCardProps) { + const { timeline, connectionStatus, onReload } = props; + + const [collapse, setCollapse] = useState(true); + + const isMobile = useMobile((mobile) => { + if (!mobile) { + switchDialog(null); + } else { + setCollapse(true); + } + }); + + const { controller, switchDialog } = useDialog({ + "full-page": ( + <FullPageDialog> + <TimelineInfoContent timeline={timeline} onReload={onReload} /> + </FullPageDialog> + ), + }); return ( <Card @@ -139,18 +179,19 @@ export default function TimelineCard(props: TimelinePageCardProps) { > <div className="timeline-card-top-right-area"> <ConnectionStatusBadge status={connectionStatus} /> - <CollapseButton collapse={collapse} onClick={toggleCollapse} /> + <CollapseButton + collapse={collapse} + onClick={() => { + const open = collapse; + setCollapse(!open); + if (isMobile && open) { + switchDialog("full-page"); + } + }} + /> </div> - {isMobile ? ( - <FullPageDialog - onBack={toggleCollapse} - show={!collapse} - contentContainerClassName="p-2" - > - {content} - </FullPageDialog> - ) : ( - <div style={{ display: collapse ? "none" : "block" }}>{content}</div> + {!collapse && !isMobile && ( + <TimelineInfoContent timeline={timeline} onReload={onReload} /> )} <DialogProvider controller={controller} /> </Card> |