@@ -17,6 +17,7 @@ export default class Clipboard extends React.Component {
1717 constructor ( props ) {
1818 super ( props ) ;
1919 this . copyToClipboard = this . copyToClipboard . bind ( this ) ;
20+ this . onClickHandler = this . onClickHandler . bind ( this ) ;
2021 this . copySuccess = this . copySuccess . bind ( this ) ;
2122 this . getTargetText = this . getTargetText . bind ( this ) ;
2223 this . loading = this . loading . bind ( this ) ;
@@ -26,6 +27,22 @@ export default class Clipboard extends React.Component {
2627 } ;
2728 }
2829
30+ onClickHandler ( ) {
31+ this . props . setProps ( { n_clicks : this . props . n_clicks + 1 } ) ;
32+ }
33+
34+ componentDidUpdate ( prevProps ) {
35+ // If the clicks has not changed, do nothing
36+ if (
37+ ! this . props . n_clicks ||
38+ this . props . n_clicks === prevProps . n_clicks
39+ ) {
40+ return ;
41+ }
42+ // If the clicks has changed, copy to clipboard
43+ this . copyToClipboard ( ) ;
44+ }
45+
2946 // stringifies object ids used in pattern matching callbacks
3047 stringifyId ( id ) {
3148 if ( typeof id !== 'object' ) {
@@ -38,9 +55,23 @@ export default class Clipboard extends React.Component {
3855 return '{' + parts . join ( ',' ) + '}' ;
3956 }
4057
41- async copySuccess ( content ) {
58+ async copySuccess ( content , htmlContent ) {
4259 const showCopiedIcon = 1000 ;
43- await clipboardAPI . writeText ( content ) ;
60+ if ( htmlContent ) {
61+ const blobHtml = new Blob ( [ htmlContent ] , { type : 'text/html' } ) ;
62+ const blobText = new Blob ( [ content ?? htmlContent ] , {
63+ type : 'text/plain' ,
64+ } ) ;
65+ const data = [
66+ new ClipboardItem ( {
67+ [ 'text/plain' ] : blobText ,
68+ [ 'text/html' ] : blobHtml ,
69+ } ) ,
70+ ] ;
71+ await navigator . clipboard . write ( data ) ;
72+ } else {
73+ await clipboardAPI . writeText ( content ) ;
74+ }
4475 this . setState ( { copied : true } ) ;
4576 await wait ( showCopiedIcon ) ;
4677 this . setState ( { copied : false } ) ;
@@ -71,20 +102,18 @@ export default class Clipboard extends React.Component {
71102 }
72103
73104 async copyToClipboard ( ) {
74- this . props . setProps ( {
75- n_clicks : this . props . n_clicks + 1 ,
76- } ) ;
77-
78105 let content ;
106+ let htmlContent ;
79107 if ( this . props . target_id ) {
80108 content = this . getTargetText ( ) ;
81109 } else {
82110 await wait ( 100 ) ; // gives time for callback to start
83111 await this . loading ( ) ;
84112 content = this . props . content ;
113+ htmlContent = this . props . html_content ;
85114 }
86- if ( content ) {
87- this . copySuccess ( content ) ;
115+ if ( content || htmlContent ) {
116+ this . copySuccess ( content , htmlContent ) ;
88117 }
89118 }
90119
@@ -106,7 +135,7 @@ export default class Clipboard extends React.Component {
106135 title = { title }
107136 style = { style }
108137 className = { className }
109- onClick = { this . copyToClipboard }
138+ onClick = { this . onClickHandler }
110139 data-dash-is-loading = {
111140 ( loading_state && loading_state . is_loading ) || undefined
112141 }
@@ -119,6 +148,7 @@ export default class Clipboard extends React.Component {
119148
120149Clipboard . defaultProps = {
121150 content : null ,
151+ html_content : null ,
122152 target_id : null ,
123153 n_clicks : 0 ,
124154} ;
@@ -137,7 +167,7 @@ Clipboard.propTypes = {
137167 target_id : PropTypes . oneOfType ( [ PropTypes . string , PropTypes . object ] ) ,
138168
139169 /**
140- * The text to be copied to the clipboard if the `target_id` is None.
170+ * The text to be copied to the clipboard if the `target_id` is None.
141171 */
142172 content : PropTypes . string ,
143173
@@ -146,6 +176,11 @@ Clipboard.propTypes = {
146176 */
147177 n_clicks : PropTypes . number ,
148178
179+ /**
180+ * The clipboard html text be copied to the clipboard if the `target_id` is None.
181+ */
182+ html_content : PropTypes . string ,
183+
149184 /**
150185 * The text shown as a tooltip when hovering over the copy icon.
151186 */
0 commit comments