@@ -5,19 +5,20 @@ import RichTextPropertyPane from './RichTextPropertyPane';
5
5
import ReactQuill , { Quill } from 'react-quill' ;
6
6
import styles from './RichText.module.scss' ;
7
7
import { IRichTextProps , IRichTextState } from './RichText.types' ;
8
- import { IconButton } from 'office-ui-fabric-react/lib/Button' ;
9
8
import { Guid } from '@microsoft/sp-core-library' ;
9
+ import * as telemetry from '../../common/telemetry' ;
10
+ import isEqual from 'lodash/isEqual' ;
11
+ import { IconButton } from 'office-ui-fabric-react/lib/Button' ;
10
12
import { TooltipHost } from 'office-ui-fabric-react/lib/Tooltip' ;
11
13
import { Dialog , DialogType , DialogFooter } from 'office-ui-fabric-react/lib/Dialog' ;
12
14
import { TextField } from 'office-ui-fabric-react/lib/TextField' ;
13
15
import { Link } from 'office-ui-fabric-react/lib/Link' ;
14
16
import { PrimaryButton , DefaultButton } from 'office-ui-fabric-react/lib/Button' ;
15
17
import { Dropdown , IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown' ;
16
18
import { Icon } from 'office-ui-fabric-react/lib/Icon' ;
17
- import { elementContains } from 'office-ui-fabric-react/lib/Utilities' ;
18
- import * as telemetry from '../../common/telemetry' ;
19
+ import { css , elementContains } from 'office-ui-fabric-react/lib/Utilities' ;
19
20
import { initializeIcons } from 'office-ui-fabric-react/lib/Icons' ;
20
- import isEqual from 'lodash/isEqual ' ;
21
+ import { Label } from 'office-ui-fabric-react/lib/Label ' ;
21
22
22
23
const TOOLBARPADDING : number = 28 ;
23
24
/**
@@ -36,6 +37,7 @@ export class RichText extends React.Component<IRichTextProps, IRichTextState> {
36
37
private _wrapperRef : HTMLDivElement = undefined ;
37
38
private _propertyPaneRef : RichTextPropertyPane = undefined ;
38
39
private _toolbarId = undefined ;
40
+ private _richTextId = undefined ;
39
41
40
42
private ddStyleOpts = [ {
41
43
key : 0 ,
@@ -128,6 +130,9 @@ export class RichText extends React.Component<IRichTextProps, IRichTextState> {
128
130
129
131
// Get a unique toolbar id
130
132
this . _toolbarId = "toolbar_" + Guid . newGuid ( ) . toString ( ) ;
133
+
134
+ // Get a unique rich text id if not provided by props
135
+ this . _richTextId = props . id ?? "richText_" + Guid . newGuid ( ) . toString ( ) ;
131
136
}
132
137
133
138
/**
@@ -139,9 +144,9 @@ export class RichText extends React.Component<IRichTextProps, IRichTextState> {
139
144
document . addEventListener ( 'click' , this . handleClickOutside ) ;
140
145
document . addEventListener ( 'focus' , this . handleClickOutside ) ;
141
146
142
- const clientRect : ClientRect = this . _wrapperRef . getBoundingClientRect ( ) ;
143
- const parentClientRect : ClientRect = this . _wrapperRef . parentElement . getBoundingClientRect ( ) ;
144
- const toolbarTop : number = clientRect . top - parentClientRect . top - TOOLBARPADDING ;
147
+ const domRect : DOMRect = this . _wrapperRef . getBoundingClientRect ( ) ;
148
+ const parentDomRect : DOMRect = this . _wrapperRef . parentElement . getBoundingClientRect ( ) ;
149
+ const toolbarTop : number = domRect . top - parentDomRect . top - TOOLBARPADDING ;
145
150
146
151
this . setState ( {
147
152
wrapperTop : toolbarTop
@@ -444,11 +449,18 @@ export class RichText extends React.Component<IRichTextProps, IRichTextState> {
444
449
const { text } = this . state ;
445
450
const { isEditMode } = this . props ;
446
451
452
+ const renderLabel : JSX . Element = (
453
+ ( this . props . onRenderLabel && this . props . onRenderLabel ( this . props ) ) ?? this . onRenderLabel ( )
454
+ ) ;
455
+
447
456
// If we're not in edit mode, display read-only version of the html
448
457
if ( ! isEditMode ) {
449
458
return (
450
- < div className = { `ql-editor ${ styles . richtext } ${ this . props . className || '' } ` }
451
- dangerouslySetInnerHTML = { { __html : text } } />
459
+ < >
460
+ { renderLabel }
461
+ < div id = { this . _richTextId } className = { css ( "ql-editor" , styles . richtext , this . props . className || null ) }
462
+ dangerouslySetInnerHTML = { { __html : text } } />
463
+ </ >
452
464
) ;
453
465
}
454
466
@@ -501,7 +513,8 @@ export class RichText extends React.Component<IRichTextProps, IRichTextState> {
501
513
Quill . register ( sizeClass , true ) ;
502
514
503
515
return (
504
- < div ref = { ( ref ) => { this . _wrapperRef = ref ; } } className = { `${ styles . richtext && this . state . editing ? 'ql-active' : '' } ${ this . props . className } ` } >
516
+ < div ref = { ( ref ) => { this . _wrapperRef = ref ; } } className = { css ( styles . richtext && this . state . editing ? 'ql-active' : null , this . props . className || null ) || null } >
517
+ { renderLabel }
505
518
< div id = { this . _toolbarId } style = { { top : this . state . wrapperTop } } >
506
519
{
507
520
showStyles && (
@@ -511,7 +524,7 @@ export class RichText extends React.Component<IRichTextProps, IRichTextState> {
511
524
onRenderCaretDown = { ( ) => < Icon className = { styles . toolbarSubmenuCaret } iconName = "CaretDownSolid8" /> }
512
525
selectedKey = { this . state . formats . header || 0 }
513
526
options = { this . ddStyleOpts }
514
- onChanged = { this . onChangeHeading }
527
+ onChange = { this . onChangeHeading }
515
528
onRenderOption = { this . onRenderStyleOption }
516
529
onRenderTitle = { this . onRenderStyleTitle }
517
530
/>
@@ -560,7 +573,7 @@ export class RichText extends React.Component<IRichTextProps, IRichTextState> {
560
573
onRenderCaretDown = { ( ) => < Icon className = { styles . toolbarSubmenuCaret } iconName = "CaretDownSolid8" /> }
561
574
selectedKey = { this . state . formats . align || 'left' }
562
575
options = { this . ddAlignOpts }
563
- onChanged = { this . onChangeAlign }
576
+ onChange = { this . onChangeAlign }
564
577
onRenderOption = { this . onRenderAlignOption }
565
578
onRenderTitle = { this . onRenderAlignTitle }
566
579
/>
@@ -575,10 +588,10 @@ export class RichText extends React.Component<IRichTextProps, IRichTextState> {
575
588
options = { this . ddListOpts }
576
589
// this option is not available yet
577
590
notifyOnReselect = { true } // allows re-selecting selected item to turn it off
578
- onChanged = { this . onChangeList }
591
+ onChange = { this . onChangeList }
579
592
onRenderOption = { this . onRenderListOption }
580
593
onRenderTitle = { this . onRenderListTitle }
581
- onRenderPlaceHolder = { this . onRenderListPlaceholder }
594
+ onRenderPlaceholder = { this . onRenderListPlaceholder }
582
595
/>
583
596
)
584
597
}
@@ -624,6 +637,7 @@ export class RichText extends React.Component<IRichTextProps, IRichTextState> {
624
637
</ div >
625
638
626
639
< ReactQuill ref = { this . linkQuill }
640
+ id = { this . _richTextId }
627
641
placeholder = { placeholder }
628
642
modules = { modules }
629
643
value = { text || '' } //property value causes issues, defaultValue does not
@@ -644,7 +658,6 @@ export class RichText extends React.Component<IRichTextProps, IRichTextState> {
644
658
{
645
659
this . renderImageDialog ( )
646
660
}
647
-
648
661
</ div >
649
662
) ;
650
663
}
@@ -666,17 +679,17 @@ export class RichText extends React.Component<IRichTextProps, IRichTextState> {
666
679
const newValue = ! this . state . formats . underline ;
667
680
this . applyFormat ( "underline" , newValue ) ;
668
681
}
669
- private onChangeHeading = ( item : IDropdownOption ) : void => {
682
+ private onChangeHeading = ( _event : React . FormEvent < HTMLDivElement > , item ? : IDropdownOption , _index ?: number ) : void => {
670
683
const newHeadingValue = item . key === 0 ? '' : item . key . toString ( ) ;
671
684
this . applyFormat ( "header" , newHeadingValue ) ;
672
685
}
673
686
674
- private onChangeAlign = ( item : IDropdownOption ) : void => {
687
+ private onChangeAlign = ( _event : React . FormEvent < HTMLDivElement > , item ? : IDropdownOption , _index ?: number ) : void => {
675
688
const newAlignValue = item . key === 'left' ? false : item . key . toString ( ) ;
676
689
this . applyFormat ( "align" , newAlignValue ) ;
677
690
}
678
691
679
- private onChangeList = ( item : IDropdownOption ) : void => {
692
+ private onChangeList = ( _event : React . FormEvent < HTMLDivElement > , item ? : IDropdownOption , _index ?: number ) : void => {
680
693
// if we're already in list mode, toggle off
681
694
const key = item . key ;
682
695
const newAlignValue = ( key === 'bullet' && this . state . formats . list === 'bullet' ) || ( key === 'numbered' && this . state . formats . list === 'numbered' ) ? false : key ;
@@ -977,4 +990,21 @@ export class RichText extends React.Component<IRichTextProps, IRichTextState> {
977
990
private linkPropertyPane = ( e : any ) : void => { // eslint-disable-line @typescript-eslint/no-explicit-any
978
991
this . _propertyPaneRef = e ;
979
992
}
993
+
994
+ /**
995
+ * Renders the label above the rich text (if specified)
996
+ */
997
+ private onRenderLabel = ( ) : JSX . Element | null => {
998
+ const { label } = this . props ;
999
+
1000
+ if ( label ) {
1001
+ return (
1002
+ < Label htmlFor = { this . _richTextId } >
1003
+ { label }
1004
+ </ Label >
1005
+ ) ;
1006
+ }
1007
+
1008
+ return null ;
1009
+ }
980
1010
}
0 commit comments