import { DefaultButton } from "office-ui-fabric-react/lib/Button" ; import { DirectionalHint , IContextualMenuItem } from "office-ui-fabric-react/lib/ContextualMenu" ; import { FocusZoneDirection } from "office-ui-fabric-react/lib/FocusZone" ; import { IIconProps } from "office-ui-fabric-react/lib/Icon" ; import * as React from "react" ; import { removeLink , setBackgroundColor , setIndentation , setTextColor , toggleBold , toggleBullet , toggleCodeBlock , toggleHeader , toggleItalic , toggleNumbering , toggleStrikethrough , toggleUnderline } from "roosterjs-editor-api" ; import { Editor } from "roosterjs-editor-core" ; import { FormatState , Indentation } from "roosterjs-editor-types" ; import { setNonCompatIndentation , toggleNonCompatBullet , toggleNonCompatNumbering } from "roosterjs-react-common" ; import { RoosterCommandBarButtonInternal , RoosterCommandBarProps , RoosterCommandBarState } from "../schema/RoosterCommandBarSchema" ; import { ColorInfo , FontColorInfoList , HighlightColorInfoList } from "./OutOfBoxCommandBarButtons.ColorInfo" ; export const RoosterCommandBarIconClassName = "rooster-command-bar-icon" ; export const RoosterCommandBarButtonRootClassName = "rooster-command-button-root" ; export const RoosterCommandBarStringKeys = { LinkPrompt : "linkPrompt" } ; export const RoosterCommmandBarButtonKeys = { Header : "header" , Bold : "bold" , Italic : "italic" , Underline : "underline" , BulletedList : "bulleted-list" , NumberedList : "numbered-list" , Link : "link" , Highlight : "highlight" , ClearFormat : "clear-format" , Emoji : "emoji" , InsertImage : "insert-image" , Indent : "indent" , Outdent : "outdent" , Strikethrough : "strikethrough" , FontColor : "font-color" , Unlink : "unlink" , Code : "code" } ; const RenderOptionsNoCustomCacheKey = null ; export const OutOfBoxCommandBarButtons = [ { key : RoosterCommmandBarButtonKeys . Bold , name : "Bold" , iconProps : _getIconProps ( "Bold" ) , canCheck : true , getChecked : ( formatState ) => formatState . isBold , handleChange : ( editor ) => toggleBold ( editor ) } , { key : RoosterCommmandBarButtonKeys . Italic , name : "Italic" , iconProps : _getIconProps ( "Italic" ) , canCheck : true , getChecked : ( formatState ) => formatState . isItalic , handleChange : ( editor ) => toggleItalic ( editor ) } , { key : RoosterCommmandBarButtonKeys . Underline , name : "Underline" , iconProps : _getIconProps ( "Underline" ) , canCheck : true , getChecked : ( formatState ) => formatState . isUnderline , handleChange : ( editor ) => toggleUnderline ( editor ) } , { key : RoosterCommmandBarButtonKeys . BulletedList , name : "Bulleted list" , iconProps : _getIconProps ( "BulletedList" ) , onRenderOptions : { customCacheKey : RenderOptionsNoCustomCacheKey , highContrastAssetName : "BulletedList" , assets : [ { name : "BulletedListText" } , { name : "BulletedListBullet" } ] } , canCheck : true , getChecked : ( formatState ) => formatState . isBullet , handleChange : ( editor , props ) => ( props . disableListWorkaround ? toggleNonCompatBullet : toggleBullet ) ( editor ) } , { key : RoosterCommmandBarButtonKeys . NumberedList , name : "Numbered list" , iconProps : _getIconProps ( "NumberedList" ) , onRenderOptions : { customCacheKey : RenderOptionsNoCustomCacheKey , highContrastAssetName : "NumberedList" , assets : [ { name : "NumberedListText" } , { name : "NumberedListNumber" } ] } , canCheck : true , getChecked : ( formatState ) => formatState . isNumbering , handleChange : ( editor , props ) => ( props . disableListWorkaround ? toggleNonCompatNumbering : toggleNumbering ) ( editor ) } , { key : RoosterCommmandBarButtonKeys . Highlight , name : "Highlight" , iconProps : _getIconProps ( "Highlight" ) , onRenderOptions : { highContrastAssetName : "FabricTextHighlightComposite" , assets : [ { name : "FontColorSwatch" , className : "highlight-swatch" } , { name : "FabricTextHighlight" } ] } , subMenuProps : { className : "rooster-command-bar-color-container" , shouldFocusOnMount : true , directionalHint : DirectionalHint . bottomLeftEdge , focusZoneProps : { direction : FocusZoneDirection . bidirectional } , items : HighlightColorInfoList . map ( ( color ) => ( { className : "rooster-command-bar-color-item" , key : color . key , title : color . title , onRender : _colorCellOnRender , data : color , handleChange : _handleChangeForHighlight } as IContextualMenuItem ) ) } } , { key : RoosterCommmandBarButtonKeys . FontColor , name : "Font color" , iconProps : _getIconProps ( "FontColor" ) , onRenderOptions : { highContrastAssetName : "FontColor" , assets : [ { name : "FontColorSwatch" , className : "color-swatch" } , { name : "FontColorA" } ] } , subMenuProps : { className : "rooster-command-bar-color-container" , shouldFocusOnMount : true , directionalHint : DirectionalHint . bottomLeftEdge , focusZoneProps : { direction : FocusZoneDirection . bidirectional } , items : FontColorInfoList . map ( ( color ) => ( { className : "rooster-command-bar-color-item" , key : color . key , title : color . title , onRender : _colorCellOnRender , data : color , handleChange : _handleChangeForFontColor } as IContextualMenuItem ) ) } } , { key : RoosterCommmandBarButtonKeys . Emoji , name : "Emoji" , iconProps : { className : `${ RoosterCommandBarIconClassName } rooster-emoji` } as IIconProps , handleChange : ( editor , props ) => props . emojiPlugin . startEmoji ( ) } , { key : RoosterCommmandBarButtonKeys . Outdent , name : "Decrease indent" , iconProps : _getIconProps ( "DecreaseIndentLegacy" ) , onRenderOptions : { customCacheKey : RenderOptionsNoCustomCacheKey , highContrastAssetName : "DecreaseIndentLegacy" , assets : [ { name : "DecreaseIndentText" } , { name : "DecreaseIndentArrow" } ] } , handleChange : ( editor , props ) => ( props . disableListWorkaround ? setNonCompatIndentation : setIndentation ) ( editor , Indentation . Decrease ) } , { key : RoosterCommmandBarButtonKeys . Indent , name : "Increase indent" , iconProps : _getIconProps ( "IncreaseIndentLegacy" ) , onRenderOptions : { customCacheKey : RenderOptionsNoCustomCacheKey , highContrastAssetName : "IncreaseIndentLegacy" , assets : [ { name : "IncreaseIndentText" } , { name : "IncreaseIndentArrow" } ] } , handleChange : ( editor , props ) => ( props . disableListWorkaround ? setNonCompatIndentation : setIndentation ) ( editor , Indentation . Increase ) } , { key : RoosterCommmandBarButtonKeys . Strikethrough , name : "Strikethrough" , iconProps : _getIconProps ( "Strikethrough" ) , getChecked : ( formatState ) => formatState . isStrikeThrough , handleChange : ( editor ) => toggleStrikethrough ( editor ) } , { key : RoosterCommmandBarButtonKeys . Header , name : "Header" , iconProps : _getIconProps ( "FontSize" ) , onRenderOptions : { assets : [ { name : "FontSize" } ] } , subMenuProps : { shouldFocusOnMount : true , directionalHint : DirectionalHint . bottomLeftEdge , items : [ { key : "header1" , name : "Header 1" , className : "rooster-command-bar-header1" , headerLevel : 1 , canCheck : true , getChecked : _getCheckedForHeader , isContextMenuItem : true , handleChange : _handleChangeForHeader , iconProps : null } , { key : "header2" , name : "Header 2" , className : "rooster-command-bar-header2" , headerLevel : 2 , canCheck : true , getChecked : _getCheckedForHeader , isContextMenuItem : true , handleChange : _handleChangeForHeader , iconProps : null } , { key : "header3" , name : "Header 3" , className : "rooster-command-bar-header3" , headerLevel : 3 , canCheck : true , getChecked : _getCheckedForHeader , isContextMenuItem : true , handleChange : _handleChangeForHeader , iconProps : null } ] as RoosterCommandBarButtonInternal [ ] } } , { key : RoosterCommmandBarButtonKeys . Code , name : "Code" , iconProps : _getIconProps ( "Embed" ) , handleChange : ( editor ) => toggleCodeBlock ( editor ) } , { key : RoosterCommmandBarButtonKeys . ClearFormat , name : "Clear format" , iconProps : _getIconProps ( "ClearFormatting" ) , onRenderOptions : { customCacheKey : RenderOptionsNoCustomCacheKey , highContrastAssetName : "ClearFormatting" , assets : [ { name : "ClearFormattingA" } , { name : "ClearFormattingEraser" } ] } , handleChange : ( editor , props ) => props . roosterCommandBarPlugin . clearFormat ( ) } , { key : RoosterCommmandBarButtonKeys . InsertImage , name : "Insert image" , iconProps : _getIconProps ( "Photo2" ) , onRenderOptions : { customCacheKey : RenderOptionsNoCustomCacheKey , assets : [ { name : "Photo2Fill" } , { name : "Photo2" } ] } } , { key : RoosterCommmandBarButtonKeys . Link , name : "Link" , iconProps : _getIconProps ( "Link" ) , handleChange : ( editor , props ) => props . roosterCommandBarPlugin . promptForLink ( ) } , { key : RoosterCommmandBarButtonKeys . Unlink , name : "Unlink" , iconProps : _getIconProps ( "RemoveLink" ) , onRenderOptions : { customCacheKey : RenderOptionsNoCustomCacheKey , highContrastAssetName : "RemoveLink" , assets : [ { name : "RemoveLinkChain" } , { name : "RemoveLinkX" } ] } , getDisabled : ( formatState ) => ! formatState . canUnlink , handleChange : ( editor ) => removeLink ( editor ) } ] ; OutOfBoxCommandBarButtons . forEach ( ( button ) => { const asset = { name : button . name } ; button . onRenderOptions = button . onRenderOptions || { customCacheKey : RenderOptionsNoCustomCacheKey , assets : [ asset ] } ; button . className = RoosterCommandBarButtonRootClassName ; } ) ; export const OutOfBoxCommandBarButtonMap = OutOfBoxCommandBarButtons . reduce ( ( result , item ) => { result [ item . key ] = item ; return result ; } , { } as { [ key ] : RoosterCommandBarButtonInternal ; } ) ; function _getIconProps ( iconName ) { return { className : RoosterCommandBarIconClassName , iconName } ; } function _getCheckedForHeader ( formatState ) { return formatState . headerLevel === this . headerLevel ; } function _handleChangeForHeader ( editor , props , state ) { if ( state . formatState . headerLevel === this . headerLevel ) { toggleHeader ( editor , 0 ) ; } else { toggleHeader ( editor , this . headerLevel ) ; } } function _colorCellOnRender ( item ) { const { color , cellBorderColor } = item . data as ColorInfo ; return ( < DefaultButton className = "rooster-command-bar-color-button" title = { item . title } key = { item . key } onClick = { ev => item . onClick ( ev as React . MouseEvent < HTMLElement > , item ) } > { cellBorderColor && < div className = "rooster-command-bar-color-cell-border" style = { { borderColor : cellBorderColor } } />} < svg className = "rooster-command-bar-color-cell" viewBox = "0 0 30 30" fill = { color } focusable = "false" > < rect width = "100%" height = "100%" / > < /svg> < /DefaultButton>); } function _handleChangeForFontColor ( editor ) { const { color } = this . data as ColorInfo ; setTextColor ( editor , color ) ; } function _handleChangeForHighlight ( editor ) { const { color } = this . data as ColorInfo ; const selection = editor . getSelection ( ) ; let backwards = false ; if ( ! selection . isCollapsed ) { const range = editor . getDocument ( ) . createRange ( ) ; range . setStart ( selection . anchorNode , selection . anchorOffset ) ; range . setEnd ( selection . focusNode , selection . focusOffset ) ; backwards = range . collapsed ; } setBackgroundColor ( editor , color ) ; if ( backwards ) { selection . collapseToStart ( ) ; } else { selection . collapseToEnd ( ) ; } }