From 400bc6f696dc098916644d4cc804c01f68d15ed8 Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Sat, 17 Dec 2022 21:06:51 +0800 Subject: [PATCH] Truncate long posts on timeline, show "Read more" 10-line clamping for now --- package-lock.json | 74 ++++++++++++++++++++++++++++++++++++++- package.json | 3 +- src/components/status.css | 24 +++++++++++++ src/components/status.jsx | 39 ++++++++++++++++++++- 4 files changed, 137 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c3886135..e1bfe48e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "preact-router": "~4.1.0", "react-intersection-observer": "~9.4.1", "string-length": "~5.0.1", + "use-resize-observer": "~9.1.0", "valtio": "~1.7.6" }, "devDependencies": { @@ -27,7 +28,7 @@ "autoprefixer": "~10.4.13", "postcss": "~8.4.20", "postcss-dark-theme-class": "~0.7.3", - "vite": "4.0.1" + "vite": "~4.0.1" } }, "node_modules/@ampproject/remapping": { @@ -848,6 +849,11 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@juggle/resize-observer": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", + "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==" + }, "node_modules/@preact/preset-vite": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@preact/preset-vite/-/preset-vite-2.5.0.tgz", @@ -1996,6 +2002,19 @@ "node": ">=0.10.0" } }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, "node_modules/react-intersection-observer": { "version": "9.4.1", "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.4.1.tgz", @@ -2042,6 +2061,15 @@ "fsevents": "~2.3.2" } }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + } + }, "node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -2205,6 +2233,18 @@ "tslib": "^2.0.3" } }, + "node_modules/use-resize-observer": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", + "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", + "dependencies": { + "@juggle/resize-observer": "^3.3.1" + }, + "peerDependencies": { + "react": "16.8.0 - 18", + "react-dom": "16.8.0 - 18" + } + }, "node_modules/use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", @@ -2836,6 +2876,11 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "@juggle/resize-observer": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", + "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==" + }, "@preact/preset-vite": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@preact/preset-vite/-/preset-vite-2.5.0.tgz", @@ -3702,6 +3747,16 @@ "loose-envify": "^1.1.0" } }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "peer": true, + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, "react-intersection-observer": { "version": "9.4.1", "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.4.1.tgz", @@ -3733,6 +3788,15 @@ "fsevents": "~2.3.2" } }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "peer": true, + "requires": { + "loose-envify": "^1.1.0" + } + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -3847,6 +3911,14 @@ "tslib": "^2.0.3" } }, + "use-resize-observer": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", + "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", + "requires": { + "@juggle/resize-observer": "^3.3.1" + } + }, "use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", diff --git a/package.json b/package.json index 1dede8e4..c0b64ec2 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "preact-router": "~4.1.0", "react-intersection-observer": "~9.4.1", "string-length": "~5.0.1", + "use-resize-observer": "~9.1.0", "valtio": "~1.7.6" }, "devDependencies": { @@ -28,7 +29,7 @@ "autoprefixer": "~10.4.13", "postcss": "~8.4.20", "postcss-dark-theme-class": "~0.7.3", - "vite": "4.0.1" + "vite": "~4.0.1" }, "postcss": { "plugins": { diff --git a/src/components/status.css b/src/components/status.css index 731dfb4d..eda87067 100644 --- a/src/components/status.css +++ b/src/components/status.css @@ -174,6 +174,30 @@ .status .content { margin-top: 8px; } +.timeline-deck .status .content { + display: -webkit-box; + -webkit-line-clamp: 10; + -webkit-box-orient: vertical; + overflow: hidden; + position: relative; +} +.timeline-deck .status .content.truncated:after { + content: attr(data-read-more); + line-height: 1; + display: inline-block; + position: absolute; + inset-block-end: 0; + inset-inline-end: 0; + color: var(--link-color); + background-color: var(--link-faded-color); + backdrop-filter: blur(4px) brightness(2); + padding: 0.5em 0.5em 0.5em 2em; + border-radius: 0 1em 1em 0; + font-size: 12px; + font-weight: bold; + text-transform: uppercase; + mask-image: linear-gradient(to right, transparent, black 2em); +} .status .content p { margin-block: 0.75em; } diff --git a/src/components/status.jsx b/src/components/status.jsx index 818dc0ba..3704e191 100644 --- a/src/components/status.jsx +++ b/src/components/status.jsx @@ -4,6 +4,7 @@ import { getBlurHashAverageColor } from 'fast-blurhash'; import mem from 'mem'; import { useEffect, useMemo, useRef, useState } from 'preact/hooks'; import { InView } from 'react-intersection-observer'; +import useResizeObserver from 'use-resize-observer'; import { useSnapshot } from 'valtio'; import Loader from '../components/loader'; @@ -619,6 +620,34 @@ function Status({ const carouselRef = useRef(null); const currentYear = new Date().getFullYear(); + const spoilerContentRef = useRef(null); + useResizeObserver({ + ref: spoilerContentRef, + onResize: () => { + if (spoilerContentRef.current) { + const { scrollHeight, clientHeight } = spoilerContentRef.current; + spoilerContentRef.current.classList.toggle( + 'truncated', + scrollHeight > clientHeight, + ); + } + }, + }); + const contentRef = useRef(null); + useResizeObserver({ + ref: contentRef, + onResize: () => { + if (contentRef.current) { + const { scrollHeight, clientHeight } = contentRef.current; + contentRef.current.classList.toggle( + 'truncated', + scrollHeight > clientHeight, + ); + } + }, + }); + const readMoreText = 'read more →'; + return (
{!!spoilerText && sensitive && ( <> -
+

{spoilerText}