feat: fold memo when content overflow (#1327)

* feat: fold memo when content overflow

* chore: update
This commit is contained in:
boojack 2023-03-09 23:32:35 +08:00 committed by GitHub
parent 8c774316ae
commit ccdcd3d154
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 24 additions and 64 deletions

View File

@ -1,5 +1,5 @@
import * as utils from "../helpers/utils"; import * as utils from "../helpers/utils";
import MemoContent, { DisplayConfig } from "./MemoContent"; import MemoContent from "./MemoContent";
import MemoResources from "./MemoResources"; import MemoResources from "./MemoResources";
import "../less/daily-memo.less"; import "../less/daily-memo.less";
@ -10,9 +10,6 @@ interface Props {
const DailyMemo: React.FC<Props> = (props: Props) => { const DailyMemo: React.FC<Props> = (props: Props) => {
const { memo } = props; const { memo } = props;
const createdTimeStr = utils.getTimeString(memo.createdTs); const createdTimeStr = utils.getTimeString(memo.createdTs);
const displayConfig: DisplayConfig = {
enableExpand: false,
};
return ( return (
<div className="daily-memo-wrapper"> <div className="daily-memo-wrapper">
@ -20,7 +17,7 @@ const DailyMemo: React.FC<Props> = (props: Props) => {
<span className="normal-text">{createdTimeStr}</span> <span className="normal-text">{createdTimeStr}</span>
</div> </div>
<div className="memo-container"> <div className="memo-container">
<MemoContent content={memo.content} displayConfig={displayConfig} /> <MemoContent content={memo.content} showFull={true} />
<MemoResources resourceList={memo.resourceList} /> <MemoResources resourceList={memo.resourceList} />
</div> </div>
<div className="split-line"></div> <div className="split-line"></div>

View File

@ -1,4 +1,3 @@
import { Tooltip } from "@mui/joy";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { memo, useEffect, useRef, useState } from "react"; import { memo, useEffect, useRef, useState } from "react";
@ -39,7 +38,6 @@ const Memo: React.FC<Props> = (props: Props) => {
const [createdTimeStr, setCreatedTimeStr] = useState<string>(getFormatedMemoTimeStr(memo.createdTs, i18n.language)); const [createdTimeStr, setCreatedTimeStr] = useState<string>(getFormatedMemoTimeStr(memo.createdTs, i18n.language));
const memoContainerRef = useRef<HTMLDivElement>(null); const memoContainerRef = useRef<HTMLDivElement>(null);
const isVisitorMode = userStore.isVisitorMode() || readonly; const isVisitorMode = userStore.isVisitorMode() || readonly;
const updatedTimeStr = getFormatedMemoTimeStr(memo.updatedTs, i18n.language);
useEffect(() => { useEffect(() => {
let intervalFlag: any = -1; let intervalFlag: any = -1;
@ -111,7 +109,7 @@ const Memo: React.FC<Props> = (props: Props) => {
} }
}; };
const handleGenMemoImageBtnClick = () => { const handleGenerateMemoImageBtnClick = () => {
showShareMemo(memo); showShareMemo(memo);
}; };
@ -203,11 +201,9 @@ const Memo: React.FC<Props> = (props: Props) => {
{memo.pinned && <div className="corner-container"></div>} {memo.pinned && <div className="corner-container"></div>}
<div className="memo-top-wrapper"> <div className="memo-top-wrapper">
<div className="status-text-container"> <div className="status-text-container">
<Tooltip title={`Updated at ${updatedTimeStr}`} placement="top" arrow> <span className="time-text" onDoubleClick={handleMemoCreatedTimeClick}>
<span className="time-text" onDoubleClick={handleMemoCreatedTimeClick}> {createdTimeStr}
{createdTimeStr} </span>
</span>
</Tooltip>
{isVisitorMode && ( {isVisitorMode && (
<a className="name-text" href={`/u/${memo.creatorId}`}> <a className="name-text" href={`/u/${memo.creatorId}`}>
@{memo.creatorName} @{memo.creatorName}
@ -238,7 +234,7 @@ const Memo: React.FC<Props> = (props: Props) => {
<Icon.Edit3 className="icon-img" /> <Icon.Edit3 className="icon-img" />
<span className="tip-text">{t("common.edit")}</span> <span className="tip-text">{t("common.edit")}</span>
</div> </div>
<div className="btn" onClick={handleGenMemoImageBtnClick}> <div className="btn" onClick={handleGenerateMemoImageBtnClick}>
<Icon.Share className="icon-img" /> <Icon.Share className="icon-img" />
<span className="tip-text">{t("common.share")}</span> <span className="tip-text">{t("common.share")}</span>
</div> </div>

View File

@ -1,18 +1,15 @@
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useUserStore } from "../store/module";
import { marked } from "../labs/marked"; import { marked } from "../labs/marked";
import Icon from "./Icon"; import Icon from "./Icon";
import "../less/memo-content.less"; import "../less/memo-content.less";
export interface DisplayConfig { const MAX_EXPAND_HEIGHT = 384;
enableExpand: boolean;
}
interface Props { interface Props {
content: string; content: string;
className?: string; className?: string;
displayConfig?: Partial<DisplayConfig>; showFull?: boolean;
onMemoContentClick?: (e: React.MouseEvent) => void; onMemoContentClick?: (e: React.MouseEvent) => void;
onMemoContentDoubleClick?: (e: React.MouseEvent) => void; onMemoContentDoubleClick?: (e: React.MouseEvent) => void;
} }
@ -23,48 +20,29 @@ interface State {
expandButtonStatus: ExpandButtonStatus; expandButtonStatus: ExpandButtonStatus;
} }
const defaultDisplayConfig: DisplayConfig = {
enableExpand: true,
};
const MemoContent: React.FC<Props> = (props: Props) => { const MemoContent: React.FC<Props> = (props: Props) => {
const { className, content, onMemoContentClick, onMemoContentDoubleClick } = props; const { className, content, showFull, onMemoContentClick, onMemoContentDoubleClick } = props;
const { t } = useTranslation(); const { t } = useTranslation();
const userStore = useUserStore();
const user = userStore.state.user;
const foldedContent = useMemo(() => {
const firstHorizontalRuleIndex = content.search(/^---$|^\*\*\*$|^___$/m);
return firstHorizontalRuleIndex !== -1 ? content.slice(0, firstHorizontalRuleIndex) : content;
}, [content]);
const [state, setState] = useState<State>({ const [state, setState] = useState<State>({
expandButtonStatus: -1, expandButtonStatus: -1,
}); });
const memoContentContainerRef = useRef<HTMLDivElement>(null); const memoContentContainerRef = useRef<HTMLDivElement>(null);
const displayConfig = {
...defaultDisplayConfig,
...props.displayConfig,
};
useEffect(() => { useEffect(() => {
if (!memoContentContainerRef) { if (showFull) {
return; return;
} }
if (displayConfig.enableExpand && user && user.localSetting.enableFoldMemo) { if (memoContentContainerRef.current) {
if (foldedContent.length !== content.length) { const height = memoContentContainerRef.current.clientHeight;
if (height > MAX_EXPAND_HEIGHT) {
setState({ setState({
...state,
expandButtonStatus: 0, expandButtonStatus: 0,
}); });
} }
} else {
setState({
...state,
expandButtonStatus: -1,
});
} }
}, [user?.localSetting.enableFoldMemo, content]); }, []);
const handleMemoContentClick = async (e: React.MouseEvent) => { const handleMemoContentClick = async (e: React.MouseEvent) => {
if (onMemoContentClick) { if (onMemoContentClick) {
@ -89,17 +67,18 @@ const MemoContent: React.FC<Props> = (props: Props) => {
<div className={`memo-content-wrapper ${className || ""}`}> <div className={`memo-content-wrapper ${className || ""}`}>
<div <div
ref={memoContentContainerRef} ref={memoContentContainerRef}
className={`memo-content-text ${state.expandButtonStatus === 0 ? "expanded" : ""}`} className={`memo-content-text ${state.expandButtonStatus === 0 ? "max-h-64 overflow-y-hidden" : ""}`}
onClick={handleMemoContentClick} onClick={handleMemoContentClick}
onDoubleClick={handleMemoContentDoubleClick} onDoubleClick={handleMemoContentDoubleClick}
> >
{marked(state.expandButtonStatus === 0 ? foldedContent : content)} {marked(content)}
</div> </div>
{state.expandButtonStatus !== -1 && ( {state.expandButtonStatus !== -1 && (
<div className="expand-btn-container"> <div className={`expand-btn-container ${state.expandButtonStatus === 0 && "!-mt-7"}`}>
<span className={`btn ${state.expandButtonStatus === 0 ? "expand-btn" : "fold-btn"}`} onClick={handleExpandBtnClick}> <div className="absolute top-0 left-0 w-full h-full blur-lg bg-white"></div>
<span className={`btn z-10 ${state.expandButtonStatus === 0 ? "expand-btn" : "fold-btn"}`} onClick={handleExpandBtnClick}>
{state.expandButtonStatus === 0 ? t("common.expand") : t("common.fold")} {state.expandButtonStatus === 0 ? t("common.expand") : t("common.fold")}
<Icon.ChevronRight className="icon-img" /> <Icon.ChevronRight className="icon-img opacity-80" />
</span> </span>
</div> </div>
)} )}

View File

@ -40,10 +40,6 @@ const PreferencesSection = () => {
await userStore.upsertUserSetting("resourceVisibility", value); await userStore.upsertUserSetting("resourceVisibility", value);
}; };
const handleIsFoldingEnabledChanged = (event: React.ChangeEvent<HTMLInputElement>) => {
userStore.upsertLocalSetting({ ...localSetting, enableFoldMemo: event.target.checked });
};
const handleDoubleClickEnabledChanged = (event: React.ChangeEvent<HTMLInputElement>) => { const handleDoubleClickEnabledChanged = (event: React.ChangeEvent<HTMLInputElement>) => {
userStore.upsertLocalSetting({ ...localSetting, enableDoubleClickEditing: event.target.checked }); userStore.upsertLocalSetting({ ...localSetting, enableDoubleClickEditing: event.target.checked });
}; };
@ -131,10 +127,6 @@ const PreferencesSection = () => {
</span> </span>
</div> </div>
<label className="form-label selector">
<span className="normal-text">{t("setting.preference-section.enable-folding-memo")}</span>
<Switch className="ml-2" checked={localSetting.enableFoldMemo} onChange={handleIsFoldingEnabledChanged} />
</label>
<label className="form-label selector"> <label className="form-label selector">
<span className="normal-text">{t("setting.preference-section.enable-double-click")}</span> <span className="normal-text">{t("setting.preference-section.enable-double-click")}</span>
<Switch className="ml-2" checked={localSetting.enableDoubleClickEditing} onChange={handleDoubleClickEnabledChanged} /> <Switch className="ml-2" checked={localSetting.enableDoubleClickEditing} onChange={handleDoubleClickEnabledChanged} />

View File

@ -126,7 +126,7 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
<div className="memo-container" ref={memoElRef}> <div className="memo-container" ref={memoElRef}>
<span className="time-text">{memo.createdAtStr}</span> <span className="time-text">{memo.createdAtStr}</span>
<div className="memo-content-wrapper"> <div className="memo-content-wrapper">
<MemoContent content={memo.content} displayConfig={{ enableExpand: false }} /> <MemoContent content={memo.content} showFull={true} />
<MemoResources resourceList={memo.resourceList} /> <MemoResources resourceList={memo.resourceList} />
</div> </div>
<div className="watermark-container"> <div className="watermark-container">

View File

@ -110,11 +110,9 @@
@apply w-full relative flex flex-row justify-start items-center; @apply w-full relative flex flex-row justify-start items-center;
> .btn { > .btn {
@apply flex flex-row justify-start items-center pl-2 pr-1 py-1 my-1 text-xs rounded-lg border bg-gray-100 dark:bg-zinc-600 border-gray-200 dark:border-zinc-600 opacity-80 shadow hover:opacity-60 cursor-pointer; @apply flex flex-row justify-start items-center pl-2 pr-1 py-1 my-2 text-xs rounded-lg border bg-gray-100 dark:bg-zinc-600 border-gray-200 dark:border-zinc-600 shadow hover:opacity-90 cursor-pointer;
&.expand-btn { &.expand-btn {
@apply mt-2;
> .icon-img { > .icon-img {
@apply rotate-90; @apply rotate-90;
} }

View File

@ -14,7 +14,6 @@ const defaultSetting: Setting = {
}; };
const defaultLocalSetting: LocalSetting = { const defaultLocalSetting: LocalSetting = {
enableFoldMemo: true,
enableDoubleClickEditing: true, enableDoubleClickEditing: true,
dailyReviewTimeOffset: 0, dailyReviewTimeOffset: 0,
}; };

View File

@ -8,7 +8,6 @@ interface Setting {
} }
interface LocalSetting { interface LocalSetting {
enableFoldMemo: boolean;
enableDoubleClickEditing: boolean; enableDoubleClickEditing: boolean;
dailyReviewTimeOffset: number; dailyReviewTimeOffset: number;
} }