@@ -20,20 +20,32 @@ import { applyTheme } from '../helpers/theme';
20
20
* @param {InlineControlsProps } props
21
21
*/
22
22
function InlineControls ( { isCollapsed, setCollapsed, linkStyle = { } } ) {
23
- const toggleLabel = isCollapsed ? 'More' : 'Less' ;
24
-
25
23
return (
26
- < div className = "Excerpt__inline-controls" >
27
- < div className = "Excerpt__toggle-container" >
24
+ < div
25
+ className = { classnames (
26
+ // Position these controls at the bottom right of the excerpt
27
+ 'absolute block right-0 bottom-0' ,
28
+ // Give extra width for larger tap target and gradient fade
29
+ // Fade transparent-to-white left-to-right to make the toggle
30
+ // control text (More/Less) more readable above other text.
31
+ // This gradient is implemented to-left to take advantage of Tailwind's
32
+ // automatic to-transparent calculation: this avoids Safari's problem
33
+ // with transparents in gradients:
34
+ // https://bugs.webkit.org/show_bug.cgi?id=150940
35
+ // https://tailwindcss.com/docs/gradient-color-stops#fading-to-transparent
36
+ 'w-20 bg-gradient-to-l from-white'
37
+ ) }
38
+ >
39
+ < div className = "flex justify-end" >
28
40
< LinkButton
29
- classes = "InlineLinkButton InlineLinkButton--underlined "
41
+ classes = "inline underline "
30
42
onClick = { ( ) => setCollapsed ( ! isCollapsed ) }
31
43
expanded = { ! isCollapsed }
32
44
title = "Toggle visibility of full excerpt text"
33
45
style = { linkStyle }
34
46
variant = "dark"
35
47
>
36
- { toggleLabel }
48
+ { isCollapsed ? 'More' : 'Less' }
37
49
</ LinkButton >
38
50
</ div >
39
51
</ div >
@@ -86,14 +98,13 @@ function Excerpt({
86
98
const [ collapsedByInlineControls , setCollapsedByInlineControls ] =
87
99
useState ( true ) ;
88
100
89
- // Container for the excerpt's content.
90
101
const contentElement = /** @type {{ current: HTMLDivElement } } */ ( useRef ( ) ) ;
91
102
92
- // Measured height of `contentElement` in pixels.
103
+ // Measured height of `contentElement` in pixels
93
104
const [ contentHeight , setContentHeight ] = useState ( 0 ) ;
94
105
95
- // Update the measured height of the content after the initial render and
96
- // when the size of the content element changes.
106
+ // Update the measured height of the content container after initial render,
107
+ // and when the size of the content element changes.
97
108
const updateContentHeight = useCallback ( ( ) => {
98
109
const newContentHeight = contentElement . current . clientHeight ;
99
110
setContentHeight ( newContentHeight ) ;
@@ -133,18 +144,48 @@ function Excerpt({
133
144
: onToggleCollapsed ( collapsed ) ;
134
145
135
146
return (
136
- < div className = "Excerpt" style = { contentStyle } >
137
- < div className = "Excerpt__content" ref = { contentElement } >
147
+ < div
148
+ data-testid = "excerpt-container"
149
+ className = { classnames (
150
+ 'relative overflow-hidden' ,
151
+ 'transition-[max-height] ease-in duration-150'
152
+ ) }
153
+ style = { contentStyle }
154
+ >
155
+ < div
156
+ className = { classnames (
157
+ // Establish new block-formatting context to prevent margin-collapsing
158
+ // in descendent elements from potentially "leaking out" and pushing
159
+ // this element down from the top of the container.
160
+ // See https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context
161
+ // See https://github.com/hypothesis/client/issues/1518
162
+ 'inline-block w-full'
163
+ ) }
164
+ data-testid = "excerpt-content"
165
+ ref = { contentElement }
166
+ >
138
167
{ children }
139
168
</ div >
140
169
< div
170
+ data-testid = "excerpt-expand"
141
171
role = "presentation"
142
172
onClick = { ( ) => setCollapsed ( false ) }
143
- className = { classnames ( {
144
- Excerpt__shadow : true ,
145
- 'Excerpt__shadow--transparent' : inlineControls ,
146
- 'is-hidden' : ! isExpandable ,
147
- } ) }
173
+ className = { classnames (
174
+ // This element provides a clickable area at the bottom of an
175
+ // expandable excerpt to expand it.
176
+ 'transition-[opacity] duration-150 ease-linear' ,
177
+ 'absolute w-full bottom-0 h-touch-minimum' ,
178
+ {
179
+ // For expandable excerpts not using inlineControls, style this
180
+ // element with a custom shadow-like gradient
181
+ 'bg-gradient-to-b from-excerptStop1 via-excerptStop2 to-excerptStop3' :
182
+ ! inlineControls && isExpandable ,
183
+ 'bg-none' : inlineControls ,
184
+ // Don't make this shadow visible OR clickable if there's nothing
185
+ // to do here (the excerpt isn't expandable)
186
+ 'opacity-0 pointer-events-none' : ! isExpandable ,
187
+ }
188
+ ) }
148
189
title = "Show the full excerpt"
149
190
/>
150
191
{ isOverflowing && inlineControls && (
0 commit comments