diff options
7 files changed, 134 insertions, 117 deletions
diff --git a/FrontEnd/src/app/index.sass b/FrontEnd/src/app/index.sass index 87616998..85c2bcdc 100644 --- a/FrontEnd/src/app/index.sass +++ b/FrontEnd/src/app/index.sass @@ -88,3 +88,6 @@ textarea .touch-action-none
touch-action: none
+
+i
+ line-height: 1
diff --git a/FrontEnd/src/app/views/timeline-common/Timeline.tsx b/FrontEnd/src/app/views/timeline-common/Timeline.tsx index aba868cb..5c8f9195 100644 --- a/FrontEnd/src/app/views/timeline-common/Timeline.tsx +++ b/FrontEnd/src/app/views/timeline-common/Timeline.tsx @@ -16,12 +16,11 @@ export interface TimelineProps { className?: string; style?: React.CSSProperties; posts: TimelinePostInfoEx[]; - onResize?: () => void; containerRef?: React.Ref<HTMLDivElement>; } const Timeline: React.FC<TimelineProps> = (props) => { - const { posts, onResize } = props; + const { posts } = props; const [showMoreIndex, setShowMoreIndex] = React.useState<number>(-1); @@ -51,7 +50,6 @@ const Timeline: React.FC<TimelineProps> = (props) => { : undefined } onClick={() => setShowMoreIndex(-1)} - onResize={onResize} /> ); }); diff --git a/FrontEnd/src/app/views/timeline-common/TimelineItem.tsx b/FrontEnd/src/app/views/timeline-common/TimelineItem.tsx index 233c81bd..74431402 100644 --- a/FrontEnd/src/app/views/timeline-common/TimelineItem.tsx +++ b/FrontEnd/src/app/views/timeline-common/TimelineItem.tsx @@ -2,44 +2,13 @@ import React from "react"; import clsx from "clsx"; import { Link } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import { Modal, Button } from "react-bootstrap"; import { useAvatar } from "@/services/user"; import { TimelinePostInfo } from "@/services/timeline"; import BlobImage from "../common/BlobImage"; - -const TimelinePostDeleteConfirmDialog: React.FC<{ - onClose: () => void; - onConfirm: () => void; -}> = ({ onClose, onConfirm }) => { - const { t } = useTranslation(); - - return ( - <Modal onHide={onClose} show centered> - <Modal.Header> - <Modal.Title className="text-danger"> - {t("timeline.post.deleteDialog.title")} - </Modal.Title> - </Modal.Header> - <Modal.Body>{t("timeline.post.deleteDialog.prompt")}</Modal.Body> - <Modal.Footer> - <Button variant="secondary" onClick={onClose}> - {t("operationDialog.cancel")} - </Button> - <Button - variant="danger" - onClick={() => { - onConfirm(); - onClose(); - }} - > - {t("operationDialog.confirm")} - </Button> - </Modal.Footer> - </Modal> - ); -}; +import TimelineLine from "./TimelineLine"; +import TimelinePostDeleteConfirmDialog from "./TimelinePostDeleteConfirmDialog"; export interface TimelineItemProps { post: TimelinePostInfo; @@ -50,7 +19,6 @@ export interface TimelineItemProps { onDelete: () => void; }; onClick?: () => void; - onResize?: () => void; className?: string; style?: React.CSSProperties; } @@ -60,7 +28,7 @@ const TimelineItem: React.FC<TimelineItemProps> = (props) => { const current = props.current === true; - const { more, onResize } = props; + const { more } = props; const avatar = useAvatar(props.post.author.username); @@ -68,31 +36,25 @@ const TimelineItem: React.FC<TimelineItemProps> = (props) => { return ( <div - className={clsx( - "timeline-item position-relative", - current && "current", - props.className - )} + className={clsx("timeline-item", current && "current", props.className)} onClick={props.onClick} style={props.style} > - <div className="timeline-line-area-container"> - <div className="timeline-line-area"> - <div className="timeline-line-segment start"></div> - <div className="timeline-line-node-container"> - <div className="timeline-line-node"></div> - </div> - <div className="timeline-line-segment end"></div> - {current && <div className="timeline-line-segment current-end" />} - </div> - </div> + <TimelineLine center="node" current={current} /> <div className="timeline-item-card"> - <div> + <div className="timeline-item-header"> <span className="mr-2"> - <small className="text-secondary white-space-no-wrap mr-2"> - {props.post.time.toLocaleString(i18n.languages)} - </small> - <small className="text-dark">{props.post.author.nickname}</small> + <span> + <Link to={"/users/" + props.post.author.username}> + <BlobImage blob={avatar} className="timeline-avatar mr-1" /> + </Link> + <small className="text-dark mr-2"> + {props.post.author.nickname} + </small> + <small className="text-secondary white-space-no-wrap"> + {props.post.time.toLocaleString(i18n.languages)} + </small> + </span> </span> {more != null ? ( <i @@ -105,16 +67,6 @@ const TimelineItem: React.FC<TimelineItemProps> = (props) => { ) : null} </div> <div className="timeline-content"> - <Link - className="float-left m-2" - to={"/users/" + props.post.author.username} - > - <BlobImage - onLoad={onResize} - blob={avatar} - className="avatar rounded" - /> - </Link> {(() => { const { content } = props.post; if (content.type === "text") { @@ -122,7 +74,6 @@ const TimelineItem: React.FC<TimelineItemProps> = (props) => { } else { return ( <BlobImage - onLoad={onResize} blob={content.data} className="timeline-content-image" /> diff --git a/FrontEnd/src/app/views/timeline-common/TimelineLine.tsx b/FrontEnd/src/app/views/timeline-common/TimelineLine.tsx new file mode 100644 index 00000000..fd7dde0a --- /dev/null +++ b/FrontEnd/src/app/views/timeline-common/TimelineLine.tsx @@ -0,0 +1,33 @@ +import clsx from "clsx"; +import React from "react"; + +export interface TimelineLineProps { + current?: boolean; + startSegmentLength?: string | number; + center: "node" | null; + className?: string; + style?: React.CSSProperties; +} + +const TimelineLine: React.FC<TimelineLineProps> = ({ + startSegmentLength, + center, + current, + className, + style, +}) => { + return ( + <div className={clsx("timeline-line", className)} style={style}> + <div className="segment start" style={{ height: startSegmentLength }} /> + {center == "node" ? ( + <div className="node-container"> + <div className="node"></div> + </div> + ) : null} + <div className="segment end"></div> + {current && <div className="segment current-end" />} + </div> + ); +}; + +export default TimelineLine; diff --git a/FrontEnd/src/app/views/timeline-common/TimelinePostDeleteConfirmDialog.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePostDeleteConfirmDialog.tsx new file mode 100644 index 00000000..b2c7a470 --- /dev/null +++ b/FrontEnd/src/app/views/timeline-common/TimelinePostDeleteConfirmDialog.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import { Modal, Button } from "react-bootstrap"; +import { useTranslation } from "react-i18next"; + +const TimelinePostDeleteConfirmDialog: React.FC<{ + onClose: () => void; + onConfirm: () => void; +}> = ({ onClose, onConfirm }) => { + const { t } = useTranslation(); + + return ( + <Modal onHide={onClose} show centered> + <Modal.Header> + <Modal.Title className="text-danger"> + {t("timeline.post.deleteDialog.title")} + </Modal.Title> + </Modal.Header> + <Modal.Body>{t("timeline.post.deleteDialog.prompt")}</Modal.Body> + <Modal.Footer> + <Button variant="secondary" onClick={onClose}> + {t("operationDialog.cancel")} + </Button> + <Button + variant="danger" + onClick={() => { + onConfirm(); + onClose(); + }} + > + {t("operationDialog.confirm")} + </Button> + </Modal.Footer> + </Modal> + ); +}; + +export default TimelinePostDeleteConfirmDialog; diff --git a/FrontEnd/src/app/views/timeline-common/TimelineTop.tsx b/FrontEnd/src/app/views/timeline-common/TimelineTop.tsx index 93a2a32c..39cfa426 100644 --- a/FrontEnd/src/app/views/timeline-common/TimelineTop.tsx +++ b/FrontEnd/src/app/views/timeline-common/TimelineTop.tsx @@ -1,5 +1,7 @@ import React from "react"; +import TimelineLine from "./TimelineLine"; + export interface TimelineTopProps { height?: number | string; children?: React.ReactElement; @@ -8,11 +10,7 @@ export interface TimelineTopProps { const TimelineTop: React.FC<TimelineTopProps> = ({ height, children }) => { return ( <div style={{ height: height }} className="timeline-top"> - <div className="timeline-line-area-container"> - <div className="timeline-line-area"> - <div className="timeline-line-segment"></div> - </div> - </div> + <TimelineLine center={null} /> {children} </div> ); diff --git a/FrontEnd/src/app/views/timeline-common/timeline-common.sass b/FrontEnd/src/app/views/timeline-common/timeline-common.sass index 1aa5e731..5af3a9ef 100644 --- a/FrontEnd/src/app/views/timeline-common/timeline-common.sass +++ b/FrontEnd/src/app/views/timeline-common/timeline-common.sass @@ -6,10 +6,6 @@ width: 100% overflow-wrap: break-word - &-item - position: relative - padding: 0.5em - $timeline-line-width: 7px $timeline-line-node-radius: 18px $timeline-line-color: $primary @@ -32,29 +28,23 @@ $timeline-line-color-current: #36c2e6 box-shadow: 0 0 20px 3px color.adjust($timeline-line-color-current, $lightness: +10%, $alpha: -0.1) .timeline-line - &-area-container - position: absolute - display: flex - justify-content: flex-end - padding-right: 5px - z-index: 1 + display: flex + flex-direction: column + align-items: center + width: 30px - top: 0em - bottom: 0em - left: 0.5em - width: 60px - transition: width 0.8s + position: absolute + z-index: 1 + left: 2em + top: 0 + bottom: 0 - @include media-breakpoint-down(sm) - width: 40px + transition: left 0.5s - &-area - display: flex - flex-direction: column - align-items: center - width: 30px + @include media-breakpoint-down(sm) + left: 1em - &-segment + .segment width: $timeline-line-width background: $timeline-line-color @@ -70,13 +60,13 @@ $timeline-line-color-current: #36c2e6 flex: 0 0 auto background: linear-gradient($timeline-line-color-current, transparent) - &-node-container + .node-container flex: 0 0 auto position: relative width: $timeline-line-node-radius height: $timeline-line-node-radius - &-node + .node width: $timeline-line-node-radius + 2 height: $timeline-line-node-radius + 2 position: absolute @@ -88,42 +78,49 @@ $timeline-line-color-current: #36c2e6 animation: 1s infinite alternate animation-name: timeline-line-node-noncurrent -.timeline-top - position: relative - text-align: right - - .timeline-line-segment - flex: 1 1 auto - .current &.timeline-item padding-bottom: 2.5em .timeline-line - &-segment - + .segment &.start background: linear-gradient($timeline-line-color, $timeline-line-color-current) - &.end background: $timeline-line-color-current - - &-node + .node animation-name: timeline-line-node-current +.timeline-top + position: relative + text-align: right + +.timeline-item + position: relative + padding: 0.5em + .timeline-item-card @extend .cru-card - @extend .clearfix position: relative - padding: 0.5em 2em 0.5em 60px - transition: background 0.5s, padding-left 0.8s + padding: 0.5em 2em 0.5em 4em + transition: background 0.5s, padding-left 0.5s @include media-breakpoint-down(sm) - padding-left: 40px + padding-left: 3em &:hover background: $gray-200 +.timeline-item-header + display: flex + align-items: center + @extend .mb-2 + +.timeline-avatar + border-radius: 50% + width: 2em + height: 2em + .timeline-item-delete-button position: absolute right: 0 @@ -171,5 +168,5 @@ $timeline-line-color-current: #36c2e6 justify-content: center .timeline - max-width: 100em - flex-grow: 1
\ No newline at end of file + max-width: 80em + flex-grow: 1 |