Truncate long posts on timeline, show "Read more"

10-line clamping for now
This commit is contained in:
Lim Chee Aun 2022-12-17 21:06:51 +08:00
parent 734a9b2b76
commit 400bc6f696
4 changed files with 137 additions and 3 deletions

74
package-lock.json generated
View file

@ -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",

View file

@ -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": {

View file

@ -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;
}

View file

@ -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 (
<div
class={`status ${
@ -714,7 +743,12 @@ function Status({
>
{!!spoilerText && sensitive && (
<>
<div class="content">
<div
class="content"
lang={language}
ref={spoilerContentRef}
data-read-more={readMoreText}
>
<p>{spoilerText}</p>
</div>
<button
@ -733,6 +767,9 @@ function Status({
)}
<div
class="content"
lang={language}
ref={contentRef}
data-read-more={readMoreText}
onClick={(e) => {
let { target } = e;
if (target.parentNode.tagName.toLowerCase() === 'a') {