Skip to content

Commit e8a17f5

Browse files
image caption button
1 parent 16e7927 commit e8a17f5

File tree

3 files changed

+69
-18
lines changed

3 files changed

+69
-18
lines changed

typescript/examples/vite_basic/src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,12 @@ const App = () => {
8888
<div
8989
style={{
9090
padding: "20px",
91-
maxWidth: "700px",
91+
maxWidth: "300px",
92+
// background: "oklch(20.5% 0 0)",
9293
margin: "0 auto",
9394
color: "oklch(90% 0 0)",
9495
display: "flex",
9596
justifyContent: "center",
96-
width: "100vw",
9797
}}
9898
>
9999
<div>

typescript/src/renderer/components/blocks/ImageBlockRenderer.tsx

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useState } from "react";
1+
import React, { useEffect, useState, useRef } from "react";
22
import { useInView } from "react-intersection-observer";
33

44
import { useRenderer } from "../../context/RendererContext";
@@ -41,7 +41,9 @@ export const ImageBlockRenderer: React.FC<ImageBlockRendererProps> = ({
4141
const [isLoading, setIsLoading] = useState<boolean>(false);
4242
const [hasError, setHasError] = useState<boolean>(false);
4343
const [showFullCaption, setShowFullCaption] = useState<boolean>(false);
44+
const [needsTruncation, setNeedsTruncation] = useState<boolean>(false);
4445
const { ref, inView } = useInView({ threshold: 0.1, triggerOnce: true });
46+
const captionRef = useRef<HTMLDivElement>(null);
4547

4648
const getImageUrl = () => {
4749
if (imageData?.type === "external") {
@@ -86,6 +88,14 @@ export const ImageBlockRenderer: React.FC<ImageBlockRendererProps> = ({
8688
};
8789
}, [inView, imageUrl, resolveImageUrl]);
8890

91+
useEffect(() => {
92+
if (captionRef.current && imageData?.caption && imageData.caption.length > 0) {
93+
const element = captionRef.current;
94+
const isOverflowing = element.scrollHeight > element.clientHeight;
95+
setNeedsTruncation(isOverflowing);
96+
}
97+
}, [imageData?.caption]);
98+
8999
return (
90100
<div
91101
{...props}
@@ -126,16 +136,30 @@ export const ImageBlockRenderer: React.FC<ImageBlockRendererProps> = ({
126136
<div className="notranslate">
127137
<figcaption className="notion-image-caption">
128138
<div
139+
ref={captionRef}
129140
className={`caption-content ${!showFullCaption ? "caption-truncated" : "caption-expanded"}`}
130141
>
131142
<RichTextRenderer richText={imageData.caption} />
132143
</div>
133-
<button
134-
className="caption-toggle-btn"
135-
onClick={() => setShowFullCaption(!showFullCaption)}
136-
>
137-
{showFullCaption ? "Show less" : "Show more"}
138-
</button>
144+
{needsTruncation && (
145+
<button
146+
className="caption-toggle-btn"
147+
onClick={() => setShowFullCaption(!showFullCaption)}
148+
>
149+
<span className="btn-text">
150+
{showFullCaption ? "Show less" : "Show more"}
151+
</span>
152+
<svg
153+
className={`btn-icon ${showFullCaption ? 'rotated' : ''}`}
154+
width="12"
155+
height="12"
156+
viewBox="0 0 12 12"
157+
fill="currentColor"
158+
>
159+
<path d="M3 4.5L6 7.5L9 4.5" stroke="currentColor" strokeWidth="1.5" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
160+
</svg>
161+
</button>
162+
)}
139163
</figcaption>
140164
</div>
141165
)}

typescript/src/renderer/styles/media.css

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -161,18 +161,45 @@
161161
}
162162

163163
button.caption-toggle-btn {
164-
background: none;
165-
border: none;
166-
color: var(--jsondoc-text-muted);
164+
background: rgba(55, 53, 47, 0.08);
165+
border: 1px solid rgba(55, 53, 47, 0.16);
166+
border-radius: 6px;
167+
color: var(--jsondoc-text-secondary);
167168
cursor: pointer;
168-
font-size: 0.875rem;
169-
margin-top: 4px;
170-
padding: 2px 4px;
171-
text-decoration: underline;
172-
transition: color 0.2s ease;
169+
font-size: 0.75rem;
170+
font-weight: 500;
171+
margin-top: 8px;
172+
padding: 6px 10px;
173+
transition: all 0.15s ease;
173174
margin-left: auto;
175+
display: inline-flex;
176+
align-items: center;
177+
gap: 4px;
178+
text-decoration: none;
179+
outline: none;
174180
}
175181

176182
.caption-toggle-btn:hover {
177-
color: var(--jsondoc-text-muted);
183+
background: rgba(55, 53, 47, 0.12);
184+
border-color: rgba(55, 53, 47, 0.24);
185+
color: var(--jsondoc-text-primary);
186+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
187+
}
188+
189+
.caption-toggle-btn:active {
190+
transform: translateY(0);
191+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
192+
}
193+
194+
.caption-toggle-btn .btn-text {
195+
line-height: 1;
196+
}
197+
198+
.caption-toggle-btn .btn-icon {
199+
/* transition: transform 0.4s ease; */
200+
flex-shrink: 0;
201+
}
202+
203+
.caption-toggle-btn .btn-icon.rotated {
204+
transform: rotate(180deg);
178205
}

0 commit comments

Comments
 (0)