From 6e4577f721ca5a815dacb1cddc958af691639f5b Mon Sep 17 00:00:00 2001 From: Steven Date: Sat, 10 Sep 2022 21:22:26 +0800 Subject: [PATCH] feat: add `MemoContent` component --- web/src/components/ArchivedMemo.tsx | 15 +--- web/src/components/DailyMemo.tsx | 17 ++-- web/src/components/Memo.tsx | 57 ++----------- web/src/components/MemoCardDialog.tsx | 12 +-- web/src/components/MemoContent.tsx | 92 +++++++++++++++++--- web/src/components/ShareMemoImageDialog.tsx | 5 +- web/src/helpers/marked.ts | 4 +- web/src/less/daily-memo.less | 8 -- web/src/less/explore.less | 12 ++- web/src/less/memo-card-dialog.less | 7 +- web/src/less/memo-content.less | 95 ++++++++++++++------- web/src/less/share-memo-image-dialog.less | 6 +- web/src/pages/Explore.tsx | 5 +- 13 files changed, 196 insertions(+), 139 deletions(-) diff --git a/web/src/components/ArchivedMemo.tsx b/web/src/components/ArchivedMemo.tsx index 33cf990b..87a8db37 100644 --- a/web/src/components/ArchivedMemo.tsx +++ b/web/src/components/ArchivedMemo.tsx @@ -3,10 +3,9 @@ import * as utils from "../helpers/utils"; import useI18n from "../hooks/useI18n"; import useToggle from "../hooks/useToggle"; import { memoService } from "../services"; -import { formatMemoContent } from "../helpers/marked"; -import Only from "./common/OnlyWhen"; -import Image from "./Image"; import toastHelper from "./Toast"; +import MemoContent from "./MemoContent"; +import MemoResources from "./MemoResources"; import "../less/memo.less"; interface Props { @@ -72,14 +71,8 @@ const ArchivedMemo: React.FC = (props: Props) => { -
- 0}> -
- {imageUrls.map((imgUrl, idx) => ( - - ))} -
-
+ + ); }; diff --git a/web/src/components/DailyMemo.tsx b/web/src/components/DailyMemo.tsx index 6b7ad40f..9799b45d 100644 --- a/web/src/components/DailyMemo.tsx +++ b/web/src/components/DailyMemo.tsx @@ -1,5 +1,5 @@ import * as utils from "../helpers/utils"; -import { formatMemoContent } from "../helpers/marked"; +import MemoContent, { DisplayConfig } from "./MemoContent"; import "../less/daily-memo.less"; interface DailyMemo extends Memo { @@ -18,22 +18,17 @@ const DailyMemo: React.FC = (props: Props) => { createdAtStr: utils.getDateTimeString(propsMemo.createdTs), timeStr: utils.getTimeString(propsMemo.createdTs), }; + const displayConfig: DisplayConfig = { + enableExpand: false, + showInlineImage: true, + }; return (
{memo.timeStr}
-
-
-
+
); diff --git a/web/src/components/Memo.tsx b/web/src/components/Memo.tsx index fbfa9f03..02fd2dfe 100644 --- a/web/src/components/Memo.tsx +++ b/web/src/components/Memo.tsx @@ -5,11 +5,12 @@ import { memo, useEffect, useRef, useState } from "react"; import "dayjs/locale/zh"; import useI18n from "../hooks/useI18n"; import { UNKNOWN_ID } from "../helpers/consts"; -import { DONE_BLOCK_REG, formatMemoContent, TODO_BLOCK_REG } from "../helpers/marked"; +import { DONE_BLOCK_REG, TODO_BLOCK_REG } from "../helpers/marked"; import { editorStateService, locationService, memoService, userService } from "../services"; import Icon from "./Icon"; import Only from "./common/OnlyWhen"; import toastHelper from "./Toast"; +import MemoContent from "./MemoContent"; import MemoResources from "./MemoResources"; import showMemoCardDialog from "./MemoCardDialog"; import showShareMemoImageDialog from "./ShareMemoImageDialog"; @@ -17,18 +18,10 @@ import "../less/memo.less"; dayjs.extend(relativeTime); -const MAX_MEMO_CONTAINER_HEIGHT = 384; - -type ExpandButtonStatus = -1 | 0 | 1; - interface Props { memo: Memo; } -interface State { - expandButtonStatus: ExpandButtonStatus; -} - export const getFormatedMemoCreatedAtStr = (createdTs: number, locale = "en"): string => { if (Date.now() - createdTs < 1000 * 60 * 60 * 24) { return dayjs(createdTs).locale(locale).fromNow(); @@ -40,26 +33,12 @@ export const getFormatedMemoCreatedAtStr = (createdTs: number, locale = "en"): s const Memo: React.FC = (props: Props) => { const memo = props.memo; const { t, locale } = useI18n(); - const [state, setState] = useState({ - expandButtonStatus: -1, - }); const [createdAtStr, setCreatedAtStr] = useState(getFormatedMemoCreatedAtStr(memo.createdTs, locale)); const memoContainerRef = useRef(null); const memoContentContainerRef = useRef(null); const isVisitorMode = userService.isVisitorMode(); useEffect(() => { - if (!memoContentContainerRef) { - return; - } - - if (Number(memoContentContainerRef.current?.clientHeight) > MAX_MEMO_CONTAINER_HEIGHT) { - setState({ - ...state, - expandButtonStatus: 0, - }); - } - let intervalFlag = -1; if (Date.now() - memo.createdTs < 1000 * 60 * 60 * 24) { intervalFlag = setInterval(() => { @@ -185,17 +164,6 @@ const Memo: React.FC = (props: Props) => { editorStateService.setEditMemoWithId(memo.id); }; - const handleExpandBtnClick = () => { - const expandButtonStatus = Boolean(!state.expandButtonStatus); - if (!expandButtonStatus) { - memoContainerRef.current?.scrollIntoView(); - } - - setState({ - expandButtonStatus: Number(expandButtonStatus) as ExpandButtonStatus, - }); - }; - return (
@@ -238,21 +206,12 @@ const Memo: React.FC = (props: Props) => {
-
- {state.expandButtonStatus !== -1 && ( -
- - {state.expandButtonStatus === 0 ? "Expand" : "Fold"} - - -
- )} + ); diff --git a/web/src/components/MemoCardDialog.tsx b/web/src/components/MemoCardDialog.tsx index 0c2fae32..b51bc782 100644 --- a/web/src/components/MemoCardDialog.tsx +++ b/web/src/components/MemoCardDialog.tsx @@ -9,6 +9,7 @@ import toastHelper from "./Toast"; import { generateDialog } from "./Dialog"; import Icon from "./Icon"; import Selector from "./common/Selector"; +import MemoContent from "./MemoContent"; import MemoResources from "./MemoResources"; import showChangeMemoCreatedTsDialog from "./ChangeMemoCreatedTsDialog"; import "../less/memo-card-dialog.less"; @@ -161,11 +162,12 @@ const MemoCardDialog: React.FC = (props: Props) => {
-
+
diff --git a/web/src/components/MemoContent.tsx b/web/src/components/MemoContent.tsx index 73238988..20f28f80 100644 --- a/web/src/components/MemoContent.tsx +++ b/web/src/components/MemoContent.tsx @@ -1,28 +1,100 @@ -import { useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { formatMemoContent } from "../helpers/marked"; +import Icon from "./Icon"; import "../less/memo-content.less"; +const defaultDisplayConfig: DisplayConfig = { + enableExpand: true, + showInlineImage: false, +}; + +export interface DisplayConfig { + enableExpand: boolean; + showInlineImage: boolean; +} + interface Props { className: string; content: string; - onMemoContentClick: (e: React.MouseEvent) => void; + displayConfig?: Partial; + onMemoContentClick?: (e: React.MouseEvent) => void; + onMemoContentDoubleClick?: (e: React.MouseEvent) => void; } +type ExpandButtonStatus = -1 | 0 | 1; + +interface State { + expandButtonStatus: ExpandButtonStatus; +} + +const MAX_MEMO_CONTAINER_HEIGHT = 384; + const MemoContent: React.FC = (props: Props) => { - const { className, content, onMemoContentClick } = props; + const { className, content, onMemoContentClick, onMemoContentDoubleClick } = props; + const [state, setState] = useState({ + expandButtonStatus: -1, + }); const memoContentContainerRef = useRef(null); + const displayConfig = { + ...defaultDisplayConfig, + ...props.displayConfig, + }; + const formatConfig = { + inlineImage: displayConfig.showInlineImage, + }; + + useEffect(() => { + if (!memoContentContainerRef) { + return; + } + + if (displayConfig.enableExpand) { + if (Number(memoContentContainerRef.current?.clientHeight) > MAX_MEMO_CONTAINER_HEIGHT) { + setState({ + ...state, + expandButtonStatus: 0, + }); + } + } + }, []); const handleMemoContentClick = async (e: React.MouseEvent) => { - onMemoContentClick(e); + if (onMemoContentClick) { + onMemoContentClick(e); + } + }; + + const handleMemoContentDoubleClick = async (e: React.MouseEvent) => { + if (onMemoContentDoubleClick) { + onMemoContentDoubleClick(e); + } + }; + + const handleExpandBtnClick = () => { + const expandButtonStatus = Boolean(!state.expandButtonStatus); + setState({ + expandButtonStatus: Number(expandButtonStatus) as ExpandButtonStatus, + }); }; return ( -
+
+
+ {state.expandButtonStatus !== -1 && ( +
+ + {state.expandButtonStatus === 0 ? "Expand" : "Fold"} + + +
+ )} +
); }; diff --git a/web/src/components/ShareMemoImageDialog.tsx b/web/src/components/ShareMemoImageDialog.tsx index 6da85202..bbf7df61 100644 --- a/web/src/components/ShareMemoImageDialog.tsx +++ b/web/src/components/ShareMemoImageDialog.tsx @@ -4,11 +4,12 @@ import toImage from "../labs/html2image"; import { ANIMATION_DURATION } from "../helpers/consts"; import useI18n from "../hooks/useI18n"; import * as utils from "../helpers/utils"; -import { formatMemoContent, IMAGE_URL_REG } from "../helpers/marked"; +import { IMAGE_URL_REG } from "../helpers/marked"; import Only from "./common/OnlyWhen"; import Icon from "./Icon"; import { generateDialog } from "./Dialog"; import toastHelper from "./Toast"; +import MemoContent from "./MemoContent"; import "../less/share-memo-image-dialog.less"; interface Props extends DialogProps { @@ -91,7 +92,7 @@ const ShareMemoImageDialog: React.FC = (props: Props) => { {memo.createdAtStr} -
+ 0}>
{imageUrls.map((imgUrl, idx) => ( diff --git a/web/src/helpers/marked.ts b/web/src/helpers/marked.ts index 01c35548..3c0c8a9a 100644 --- a/web/src/helpers/marked.ts +++ b/web/src/helpers/marked.ts @@ -43,10 +43,10 @@ const defaultFormatterConfig: FormatterConfig = { inlineImage: false, }; -const formatMemoContent = (content: string, addtionConfig?: Partial) => { +const formatMemoContent = (content: string, additionConfig?: Partial) => { const config = { ...defaultFormatterConfig, - ...addtionConfig, + ...additionConfig, }; const tempElement = document.createElement("div"); tempElement.innerHTML = parseMarkedToHtml(escape(content)); diff --git a/web/src/less/daily-memo.less b/web/src/less/daily-memo.less index 993b2917..3dd123eb 100644 --- a/web/src/less/daily-memo.less +++ b/web/src/less/daily-memo.less @@ -19,13 +19,5 @@ > .memo-content-container { @apply flex flex-col justify-start items-start w-full overflow-x-hidden p-0 text-base; - - > .images-container { - @apply flex flex-col justify-start items-start mt-1 w-full; - - > img { - @apply w-full h-auto rounded mb-2 last:mb-0; - } - } } } diff --git a/web/src/less/explore.less b/web/src/less/explore.less index b8741c16..2c1c1b95 100644 --- a/web/src/less/explore.less +++ b/web/src/less/explore.less @@ -11,8 +11,16 @@ @apply sticky top-0 z-10 max-w-2xl w-full min-h-full flex flex-row justify-between items-center px-4 sm:pr-6 pt-6 mb-2; background-color: #f6f5f4; - > .logo-img { - @apply h-14 w-auto; + > .title-container { + @apply flex flex-row justify-start items-center; + + > .logo-img { + @apply h-12 sm:h-14 w-auto mr-1; + } + + > .title-text { + @apply text-xl sm:text-3xl font-mono text-gray-700; + } } > .action-button-container { diff --git a/web/src/less/memo-card-dialog.less b/web/src/less/memo-card-dialog.less index c84808d8..c72b8cdb 100644 --- a/web/src/less/memo-card-dialog.less +++ b/web/src/less/memo-card-dialog.less @@ -55,12 +55,7 @@ } > .memo-container { - .flex(column, flex-start, flex-start); - @apply w-full pt-2; - - > .memo-content-text { - @apply w-full text-base; - } + @apply w-full flex flex-col justify-start items-start pt-2; } > .normal-text { diff --git a/web/src/less/memo-content.less b/web/src/less/memo-content.less index d5eea874..24aa9442 100644 --- a/web/src/less/memo-content.less +++ b/web/src/less/memo-content.less @@ -1,41 +1,78 @@ @import "./mixin.less"; -.memo-content-text { - @apply w-full whitespace-pre-wrap break-words text-base leading-7; +.memo-content-wrapper { + @apply w-full flex flex-col justify-start items-start; - > p { - @apply inline-block w-full h-auto mb-1 last:mb-0 text-base leading-7 whitespace-pre-wrap break-words; + > .memo-content-text { + @apply w-full whitespace-pre-wrap break-words text-base leading-7; + + &.expanded { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 8; + overflow: hidden; + } + + > p { + @apply inline-block w-full h-auto mb-1 last:mb-0 text-base leading-7 whitespace-pre-wrap break-words; + } + + .img { + @apply float-left max-w-full w-full; + } + + .tag-span { + @apply inline-block w-auto font-mono text-blue-600; + } + + .memo-link-text { + @apply inline-block text-blue-600 cursor-pointer font-bold border-none no-underline hover:opacity-80; + } + + .link { + @apply inline-block text-blue-600 cursor-pointer underline break-all hover:opacity-80; + } + + .counter-block, + .todo-block { + @apply float-left inline-block box-border text-center w-7 font-mono select-none; + } + + .todo-block { + @apply w-4 h-4 leading-4 border rounded box-border text-lg cursor-pointer shadow-inner hover:opacity-80; + margin-top: 6px; + margin-left: 6px; + margin-right: 6px; + } + + pre { + @apply w-full mt-1 py-2 px-3 rounded text-sm bg-gray-100 whitespace-pre-wrap; + } } - .img { - @apply float-left max-w-full w-full; - } + > .expand-btn-container { + @apply w-full relative flex flex-row justify-start items-center; - .tag-span { - @apply inline-block w-auto font-mono text-blue-600; - } + > .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 border-gray-200 opacity-80 shadow hover:opacity-60; - .memo-link-text { - @apply inline-block text-blue-600 cursor-pointer font-bold border-none no-underline hover:opacity-80; - } + &.expand-btn { + @apply mt-2; - .link { - @apply inline-block text-blue-600 cursor-pointer underline break-all hover:opacity-80; - } + > .icon-img { + @apply rotate-90; + } + } - .counter-block, - .todo-block { - @apply float-left inline-block box-border text-center w-7 font-mono select-none; - } + &.fold-btn { + > .icon-img { + @apply -rotate-90; + } + } - .todo-block { - @apply w-4 h-4 leading-4 border rounded box-border text-lg cursor-pointer shadow-inner hover:opacity-80; - margin-top: 6px; - margin-left: 6px; - margin-right: 6px; - } - - pre { - @apply w-full mt-1 py-2 px-3 rounded text-sm bg-gray-100 whitespace-pre-wrap; + > .icon-img { + @apply w-4 h-auto ml-1 transition-all; + } + } } } diff --git a/web/src/less/share-memo-image-dialog.less b/web/src/less/share-memo-image-dialog.less index 721a0645..63e712e9 100644 --- a/web/src/less/share-memo-image-dialog.less +++ b/web/src/less/share-memo-image-dialog.less @@ -44,11 +44,11 @@ } > .time-text { - @apply w-full px-6 pt-5 text-xs text-gray-500 bg-white; + @apply w-full px-6 pt-5 pb-2 text-xs text-gray-500 bg-white; } - > .memo-content-text { - @apply w-full pt-2 pb-4 px-6 text-base bg-white; + > .memo-content-wrapper { + @apply w-full px-6 text-base bg-white pb-2; } > .images-container { diff --git a/web/src/pages/Explore.tsx b/web/src/pages/Explore.tsx index 2f739424..e9db1959 100644 --- a/web/src/pages/Explore.tsx +++ b/web/src/pages/Explore.tsx @@ -47,7 +47,10 @@ const Explore = () => {
- +
+ + Explore +
{user ? (