zoukankan      html  css  js  c++  java
  • reactjs simple text editor

      1 import React, { Component } from 'react'
      2 import PubSub from 'pubsub'
      3 import GlobalVars from 'globalVars'
      4 import styles from './main.css'
      5 
      6 // globalVars.runMode
      7 class Text extends Component{
      8     static defaultProps = {
      9         text: '文案内容'
     10             
     11     };
     12     constructor(props, context) {
     13         super(props)
     14         this.state = {
     15                 'content' : props.content
     16                ,'Styles' : props.Styles
     17                ,'editMode' : false 
     18             }
     19 
     20         PubSub.subscribe('textEditorBar' , (evt) => {
     21             var workingEditor = evt.data
     22             if (workingEditor != this.editorID){
     23                 this.hideEditor()
     24             }
     25         })
     26     }
     27     componentWillReceiveProps(nextProps){
     28         this.setState({'Styles' : nextProps.Styles})
     29     }
     30 
     31     emitChange(evt ){
     32         //https://github.com/sstur/react-rte/blob/master/src/SimpleRichTextEditor.js
     33         var new_content = this.textInput.innerHTML
     34         if (new_content != this.state.content){
     35             this.setState({'content' : new_content})
     36         }
     37     }
     38 
     39     showEditor(evt){
     40         evt.preventDefault()
     41         this.setState({'editMode' : {'palette' : false}})
     42     }
     43     hideEditor(evt){
     44        this.setState({'editMode' : false})
     45     }
     46     saveSelection() {
     47         //https://github.com/mindmup/bootstrap-wysiwyg/blob/master/bootstrap-wysiwyg.js
     48     
     49         var sel = window.getSelection()
     50         if (sel.getRangeAt && sel.rangeCount) {
     51             this.selectedRange = sel.getRangeAt(0)
     52         }
     53     }
     54     updateToolbar() {
     55         var btns = document.querySelectorAll('.' + styles.textEditor + ' li[data-tag]')
     56 
     57         for (var i=0;i< btns.length;i++){
     58             var tag = btns[i].dataset.tag
     59             
     60             if (document.queryCommandState(tag)){
     61                 btns[i].classList.add(styles.editActive)
     62             }else {
     63                 btns[i].classList.remove(styles.editActive)
     64             }
     65         }
     66     }
     67     emitKeyUp() {
     68         this.saveSelection()
     69         this.updateToolbar()
     70     }
     71     emitPaste(evt) {
     72         evt.preventDefault()
     73         var content = this.formatContent(evt.clipboardData.getData('Text') , {'nl2br':true})
     74         this.restoreSelection()
     75         document.execCommand('insertHTML', false, content)
     76         this.saveSelection()
     77     }
     78     formatContent(content , opt){
     79         opt = opt || {}
     80         if (!content) return ''
     81         if (opt.nl2br){
     82             //content = content.replace(/<(?:.|
    )*?>/gm, '').replace(/
    /g,'</br/>')
     83             content = content.replace(/
    /g,'</br/>')
     84         }
     85         return content
     86     }
     87     
     88     restoreSelection() {
     89         var selection = window.getSelection()
     90         if (this.selectedRange) {
     91             try {
     92                 selection.removeAllRanges()
     93             } catch (ex) {
     94                 document.body.createTextRange().select()
     95                 document.selection.empty()
     96             }
     97 
     98             selection.addRange(this.selectedRange)
     99         }
    100     }
    101 
    102 
    103     setStyles(tag ,new_val){
    104         this.restoreSelection()
    105         document.execCommand(tag ,false , new_val || null)
    106         this.saveSelection()
    107         this.updateToolbar()
    108     }
    109     getSelectionHtml(){
    110         var userSelection
    111         if (window.getSelection) {
    112             // W3C Ranges
    113             userSelection = window.getSelection()
    114             // Get the range:
    115             if (userSelection.getRangeAt){
    116                 var range = userSelection.getRangeAt(0)
    117                 var container = range.commonAncestorContainer
    118                 if (container.nodeType == 3) {
    119                     container = container.parentNode
    120                     return container.outerHTML
    121                 }
    122                 //if (container.nodeName === "A") {alert ("Yes, it's an anchor!");}
    123                 var clonedSelection = range.cloneContents()
    124                 var div = document.createElement('div')
    125                 div.appendChild(clonedSelection)
    126                 return div.innerHTML
    127             }
    128         } else if (document.selection) {
    129             // Explorer selection, return the HTML
    130             userSelection = document.selection.createRange()
    131             return userSelection.htmlText
    132         } else {
    133             return ''
    134         }
    135     }
    136     setLink(){
    137         this.restoreSelection()
    138         var wSelf = this
    139         var tmp = document.createElement('div')
    140         tmp.innerHTML = this.getSelectionHtml() 
    141         var link = tmp.getElementsByTagName('a')
    142         PubSub.publish(
    143                 'widgetEditLink' 
    144                 , {'link' : link[0] 
    145                     ,'cbk' : (new_val) => {
    146                         this.restoreSelection()
    147                         var sText = window.getSelection()
    148                         if (new_val.link){ 
    149                             document.execCommand('createlink', false, new_val.link)
    150                             //document.execCommand('insertHTML', false, '<a href="' + new_val.link + '" target="' + (new_val.target || '_blank') + '">' + sText + '</a>')
    151                         }else{
    152                             var range = window.getSelection().getRangeAt(0)
    153                             var container = range.commonAncestorContainer
    154                             if (container.nodeType == 3) {
    155                                 container = container.parentNode
    156                             }
    157                             if (container.nodeName === "A") {
    158                                 container.outerHTML = container.innerHTML
    159                             }
    160 
    161                         }
    162                         this.saveSelection()
    163                     }
    164                 } 
    165         ,this)
    166 
    167     }
    168     setWholeStyles(tag , val){
    169         var Styles = {...this.state.Styles }|| {}
    170         if (val){
    171             Styles[tag] = val
    172         } else {
    173             delete Styles[tag]
    174         }
    175         this.setState({'Styles' : Styles})
    176     }
    177     shouldComponentUpdate(newProps, newState){
    178         if (this.props.setProps) { 
    179             var state_clone = {...newState}
    180             delete state_clone.editMode
    181             this.props.setProps(state_clone) 
    182         }
    183         return true
    184     }
    185     setFontSize(evt){
    186         this.setWholeStyles('fontSize' , evt.target.value)
    187     }
    188     setForeColor(evt){
    189         this.setWholeStyles('color' , evt.target.dataset.color)
    190     }
    191     palette(){
    192         var {editMode} = this.state || {}
    193         this.setState({'editMode' : {'palette': !editMode.palette}})
    194     }
    195 
    196     render(){
    197         var {content ,Styles ,editMode}  = this.state
    198         Styles = Styles || {}
    199         content = this.formatContent(content) 
    200 
    201         var fontsize_options = {
    202             's' : styles.textSmall 
    203             ,'n' : styles.textNormal
    204             , 'l' : styles.textLarge
    205         }
    206         var fontsize_state = (Styles.fontSize in fontsize_options) ? Styles.fontSize :'n'
    207         var wrapper_cls = styles.textWrapper
    208         var fontsize_cls = fontsize_options[fontsize_state] 
    209         if (fontsize_cls) wrapper_cls += ' ' + fontsize_cls
    210 
    211         
    212         var StylesClone = {...Styles}
    213         delete StylesClone.fontSize
    214 
    215         if ('edit' == GlobalVars.runMode){
    216             if (editMode) {
    217                 editMode = editMode || {}
    218                 //fontSize foreColor
    219                 var size_options = []
    220                 ;[{'txt' :'' ,'val' : 's'} 
    221                 ,{'txt' :'普通' ,'val' : 'n'}
    222                 ,{'txt' : '' ,'val' : 'l'}].forEach((item , i) => {
    223                     size_options.push(<option key={i} value={item.val}>{item.txt}</option>)
    224                 })
    225         
    226                 var color_options = []
    227                 ;['#f00','#ccc' , '#0ff','#f69'].forEach((color , i) => {
    228                     color_options.push(<li key={i} onClick={this.setForeColor.bind(this)} data-color={color} style={{'color':color}}>{color}</li>)
    229                 
    230                 })
    231 
    232                 var palette_style = {} 
    233                 if (editMode.palette) {
    234                     palette_style.display = 'block'
    235                 } 
    236                 var edit_btn = ( 
    237                     <ul className={styles.textEditor}>
    238                         <li data-tag='bold'  onClick={this.setStyles.bind(this,'bold')}>B</li> 
    239                         <li data-tag='italic'  onClick={this.setStyles.bind(this,'italic')}>I</li> 
    240                         <li data-tag='underline'  onClick={this.setStyles.bind(this,'underline')}>U</li> 
    241 
    242                         <li data-tag='justifyLeft' onClick={this.setStyles.bind(this,'justifyLeft')}>L</li> 
    243                         <li data-tag='justifyCenter' onClick={this.setStyles.bind(this,'justifyCenter')}>C</li> 
    244                         <li data-tag='justifyRight'  onClick={this.setStyles.bind(this,'justifyRight')}>R</li> 
    245                         <li data-tag='justifyFull' onClick={this.setStyles.bind(this,'justifyFull')}>F</li> 
    246                         
    247                         
    248                         <li onClick={this.setLink.bind(this)}>Link</li> 
    249         
    250                         <li onClick={this.palette.bind(this)} className={styles.textColorEditor}>
    251                             Color
    252                             <ul style={palette_style}>
    253                                 <li  onClick={this.setForeColor.bind(this)} data-color='' style={{'color':'grey'}}>默认</li>
    254                                 {color_options} 
    255                             </ul>
    256                         </li>
    257                         <li>
    258                         <select value={fontsize_state}  onChange={this.setFontSize.bind(this)}>
    259                             {size_options}
    260                        </select> 
    261                         </li>
    262                     </ul>)
    263             }
    264             var holder_cls = `${styles.textHolder} textEditorHolder`
    265             this.editorID = uuid()
    266             return (
    267                 <div className={holder_cls} data-editorid={this.editorID}>
    268                     <div
    269                         contentEditable
    270                         suppressContentEditableWarning
    271                         ref={(input) => this.textInput = input}
    272                         onInput={this.emitChange.bind(this)} 
    273                         onBlur={this.emitChange.bind(this )}
    274                         onClick={this.showEditor.bind(this)}
    275                         onKeyUp={this.emitKeyUp.bind(this)}
    276                         onMouseUp={this.emitKeyUp.bind(this)}
    277                         onPaste={this.emitPaste.bind(this)}
    278                         style={StylesClone} 
    279                         className={wrapper_cls}
    280                         dangerouslySetInnerHTML={{__html:content}}
    281                     ></div>
    282                     {edit_btn}
    283                 </div>
    284             ) 
    285         
    286         }else {
    287             return (
    288                 <div style={StylesClone} className={wrapper_cls}>{content}</div>
    289             ) 
    290         }
    291     }
    292 }
    293 export default Text
  • 相关阅读:
    设置VMware Player中的虚拟机和宿主机共享文件
    C++20尝鲜:概念(Concepts)
    ReactiveX 学习笔记(31)ReactiveUI 使用笔记
    TypeScript语言学习笔记(4)枚举
    TypeScript语言学习笔记(3)函数,泛型
    趣味编程:FizzBuzz(Haskell版)
    Haskell语言学习笔记(95)Semiring
    Haskell语言学习笔记(94)Enum Bounded
    ReactiveX 学习笔记(30)操作符辨析
    ReactiveX 学习笔记(29)使用 RxCpp(RxQt)+ Qt 进行 GUI 编程
  • 原文地址:https://www.cnblogs.com/vaal-water/p/6047809.html
Copyright © 2011-2022 走看看