@@ -18,13 +18,16 @@ const render = LitHtml.render;
1818export interface MarkdownViewData {
1919 tokens : Marked . Marked . Token [ ] ;
2020 renderer ?: MarkdownLitRenderer ;
21+ animationEnabled ?: boolean ;
2122}
2223
2324export class MarkdownView extends HTMLElement {
2425 readonly #shadow = this . attachShadow ( { mode : 'open' } ) ;
2526
2627 #tokenData: readonly Marked . Marked . Token [ ] = [ ] ;
2728 #renderer = new MarkdownLitRenderer ( ) ;
29+ #animationEnabled = false ;
30+ #isAnimating = false ;
2831
2932 connectedCallback ( ) : void {
3033 this . #shadow. adoptedStyleSheets = [ markdownViewStyles ] ;
@@ -35,11 +38,53 @@ export class MarkdownView extends HTMLElement {
3538 if ( data . renderer ) {
3639 this . #renderer = data . renderer ;
3740 }
41+
42+ if ( data . animationEnabled ) {
43+ this . #animationEnabled = true ;
44+ this . #renderer. setCustomClasses ( {
45+ paragraph : 'pending' ,
46+ heading : 'pending' ,
47+ list_item : 'pending' ,
48+ } ) ;
49+ } else {
50+ this . #animationEnabled = false ;
51+ this . #renderer. setCustomClasses ( { } ) ;
52+ }
53+
3854 this . #update( ) ;
3955 }
4056
57+ #animate( ) : void {
58+ if ( this . #isAnimating) {
59+ return ;
60+ }
61+
62+ this . #isAnimating = true ;
63+ const reveal = ( ) : void => {
64+ const pendingElement = this . #shadow. querySelector ( '.pending' ) ;
65+ if ( ! pendingElement ) {
66+ this . #isAnimating = false ;
67+ return ;
68+ }
69+
70+ pendingElement . addEventListener ( 'animationend' , ( ) => {
71+ pendingElement . classList . remove ( 'animating' ) ;
72+ reveal ( ) ;
73+ } , { once : true } ) ;
74+
75+ pendingElement . classList . remove ( 'pending' ) ;
76+ pendingElement . classList . add ( 'animating' ) ;
77+ } ;
78+
79+ reveal ( ) ;
80+ }
81+
4182 #update( ) : void {
4283 this . #render( ) ;
84+
85+ if ( this . #animationEnabled) {
86+ this . #animate( ) ;
87+ }
4388 }
4489
4590 #render( ) : void {
@@ -66,6 +111,18 @@ declare global {
66111 * Default renderer is used for the IssuesPanel and allows only well-known images and links to be embedded.
67112 */
68113export class MarkdownLitRenderer {
114+ #customClasses: Record < string , string > = { } ;
115+
116+ setCustomClasses ( customClasses : Record < Marked . Marked . Token [ 'type' ] , string > ) : void {
117+ this . #customClasses = customClasses ;
118+ }
119+
120+ #customClassMapForToken( type : Marked . Marked . Token [ 'type' ] ) : LitHtml . Directive . DirectiveResult {
121+ return LitHtml . Directives . classMap ( {
122+ [ this . #customClasses[ type ] ] : this . #customClasses[ type ] ,
123+ } ) ;
124+ }
125+
69126 renderChildTokens ( token : Marked . Marked . Token ) : LitHtml . TemplateResult [ ] {
70127 if ( 'tokens' in token && token . tokens ) {
71128 return token . tokens . map ( token => this . renderToken ( token ) ) ;
@@ -102,25 +159,27 @@ export class MarkdownLitRenderer {
102159 }
103160
104161 renderHeading ( heading : Marked . Marked . Tokens . Heading ) : LitHtml . TemplateResult {
162+ const customClass = this . #customClassMapForToken( 'heading' ) ;
105163 switch ( heading . depth ) {
106164 case 1 :
107- return html `< h1 > ${ this . renderText ( heading ) } </ h1 > ` ;
165+ return html `< h1 class = ${ customClass } > ${ this . renderText ( heading ) } </ h1 > ` ;
108166 case 2 :
109- return html `< h2 > ${ this . renderText ( heading ) } </ h2 > ` ;
167+ return html `< h2 class = ${ customClass } > ${ this . renderText ( heading ) } </ h2 > ` ;
110168 case 3 :
111- return html `< h3 > ${ this . renderText ( heading ) } </ h3 > ` ;
169+ return html `< h3 class = ${ customClass } > ${ this . renderText ( heading ) } </ h3 > ` ;
112170 case 4 :
113- return html `< h4 > ${ this . renderText ( heading ) } </ h4 > ` ;
171+ return html `< h4 class = ${ customClass } > ${ this . renderText ( heading ) } </ h4 > ` ;
114172 case 5 :
115- return html `< h5 > ${ this . renderText ( heading ) } </ h5 > ` ;
173+ return html `< h5 class = ${ customClass } > ${ this . renderText ( heading ) } </ h5 > ` ;
116174 default :
117- return html `< h6 > ${ this . renderText ( heading ) } </ h6 > ` ;
175+ return html `< h6 class = ${ customClass } > ${ this . renderText ( heading ) } </ h6 > ` ;
118176 }
119177 }
120178
121179 renderCodeBlock ( token : Marked . Marked . Tokens . Code ) : LitHtml . TemplateResult {
122180 // clang-format off
123181 return html `< devtools-code-block
182+ class =${ this . #customClassMapForToken( 'code' ) }
124183 .code =${ this . unescape ( token . text ) }
125184 .codeLang=${ token . lang || '' } >
126185 </ devtools-code-block > ` ;
@@ -130,39 +189,43 @@ export class MarkdownLitRenderer {
130189 templateForToken ( token : Marked . Marked . MarkedToken ) : LitHtml . TemplateResult | null {
131190 switch ( token . type ) {
132191 case 'paragraph' :
133- return html `< p > ${ this . renderChildTokens ( token ) } </ p > ` ;
192+ return html `< p class = ${ this . #customClassMapForToken ( 'paragraph' ) } > ${ this . renderChildTokens ( token ) } </ p > ` ;
134193 case 'list' :
135- return html `< ul > ${ token . items . map ( token => {
194+ return html `< ul class = ${ this . #customClassMapForToken ( 'list' ) } > ${ token . items . map ( token => {
136195 return this . renderToken ( token ) ;
137196 } ) } </ ul > `;
138197 case 'list_item' :
139- return html `< li > ${ this . renderChildTokens ( token ) } </ li > ` ;
198+ return html `< li class = ${ this . #customClassMapForToken ( 'list_item' ) } > ${ this . renderChildTokens ( token ) } </ li > ` ;
140199 case 'text' :
141200 return this . renderText ( token ) ;
142201 case 'codespan' :
143- return html `< code > ${ this . unescape ( token . text ) } </ code > ` ;
202+ return html `< code class = ${ this . #customClassMapForToken ( 'codespan' ) } > ${ this . unescape ( token . text ) } </ code > ` ;
144203 case 'code' :
145204 return this . renderCodeBlock ( token ) ;
146205 case 'space' :
147206 return html `` ;
148207 case 'link' :
149- return html `< devtools-markdown-link .data =${ {
208+ return html `< devtools-markdown-link
209+ class =${ this . #customClassMapForToken( 'link' ) }
210+ .data =${ {
150211 key :
151212 token . href , title : token . text ,
152213 }
153214 } > </ devtools-markdown-link > `;
154215 case 'image' :
155- return html `< devtools-markdown-image .data =${ {
216+ return html `< devtools-markdown-image
217+ class =${ this . #customClassMapForToken( 'image' ) }
218+ .data =${ {
156219 key :
157220 token . href , title : token . text ,
158221 }
159222 } > </ devtools-markdown-image > `;
160223 case 'heading' :
161224 return this . renderHeading ( token ) ;
162225 case 'strong' :
163- return html `< strong > ${ this . renderText ( token ) } </ strong > ` ;
226+ return html `< strong class = ${ this . #customClassMapForToken ( 'strong' ) } > ${ this . renderText ( token ) } </ strong > ` ;
164227 case 'em' :
165- return html `< em > ${ this . renderText ( token ) } </ em > ` ;
228+ return html `< em class = ${ this . #customClassMapForToken ( 'em' ) } > ${ this . renderText ( token ) } </ em > ` ;
166229 default :
167230 return null ;
168231 }
0 commit comments