import ClipboardJS from "clipboard" ; import { EMPTY , Observable , Subject , defer , distinctUntilChanged , distinctUntilKeyChanged , finalize , map , mergeWith , switchMap , takeLast , takeUntil , tap } from "rxjs" ; import { feature } from "~/_" ; import { getElementContentSize , watchElementSize } from "~/browser" ; import { renderClipboardButton } from "~/templates" ; import { Component } from "../../_" ; import { Annotation , mountAnnotationList } from "../annotation" ; /**
 * Code block
 */ export interface CodeBlock { scrollable : boolean ; } /**
 * Mount options
 */ interface MountOptions { print$ : Observable < boolean > ; } /**
 * Global sequence number for Clipboard.js integration
 */ let sequence = 0 ; param el returns function findCandidateList ( el ) { if ( el . nextElementSibling ) { const sibling = el . nextElementSibling as HTMLElement ; if ( sibling . tagName === "OL" ) return sibling ; else if ( sibling . tagName === "P" && ! sibling . children . length ) return findCandidateList ( sibling ) ; } return undefined ; } param el returns export function watchCodeBlock ( el ) { return watchElementSize ( el ) . pipe ( map ( ( { width } ) => { const content = getElementContentSize ( el ) ; return { scrollable : content . width > width } ; } ) , distinctUntilKeyChanged ( "scrollable" ) ) ; } param el param options returns export function mountCodeBlock ( el , options ) { const { matches : hover } = matchMedia ( "(hover)" ) ; return defer ( ( ) => { const push$ = new Subject < CodeBlock > ( ) ; push$ . subscribe ( ( { scrollable } ) => { if ( scrollable && hover ) el . setAttribute ( "tabindex" , "0" ) ; else el . removeAttribute ( "tabindex" ) ; } ) ; if ( ClipboardJS . isSupported ( ) ) { const parent = el . closest ( "pre" ) ! ; parent . id = `__code_${ ++ sequence }` ; parent . insertBefore ( renderClipboardButton ( parent . id ) , el ) ; } const container = el . closest ( [ ":not(td):not(.code) > .highlight" , ".highlighttable" ] . join ( ", " ) ) ; if ( container instanceof HTMLElement ) { const list = findCandidateList ( container ) ; if ( typeof list !== "undefined" && ( container . classList . contains ( "annotate" ) || feature ( "content.code.annotate" ) ) ) { const annotations$ = mountAnnotationList ( list , el , options ) ; return watchCodeBlock ( el ) . pipe ( tap ( state => push$ . next ( state ) ) , finalize ( ( ) => push$ . complete ( ) ) , map ( state => ( { ref : el , ... state } ) ) , mergeWith ( watchElementSize ( container ) . pipe ( takeUntil ( push$ . pipe ( takeLast ( 1 ) ) ) , map ( ( { width , height } ) => width && height ) , distinctUntilChanged ( ) , switchMap ( active => active ? annotations$ : EMPTY ) ) ) ) ; } } return watchCodeBlock ( el ) . pipe ( tap ( state => push$ . next ( state ) ) , finalize ( ( ) => push$ . complete ( ) ) , map ( state => ( { ref : el , ... state } ) ) ) ; } ) ; }