├── .gitignore ├── .npmignore ├── .travis.yml ├── Dockerfile ├── Readme.md ├── _config.yml ├── configs └── bs-config.js ├── demo ├── src │ ├── basic.html │ ├── basic.jsx │ ├── custombutton.html │ ├── custombutton.jsx │ ├── edit_inline.html │ ├── edit_inline.jsx │ ├── full_editor.html │ ├── full_editor.jsx │ ├── image.jpg │ ├── index.html │ ├── init_on_button.html │ ├── init_on_button.jsx │ ├── init_on_image.html │ ├── init_on_image.jsx │ ├── init_on_input.html │ ├── init_on_input.jsx │ ├── init_on_link.html │ ├── init_on_link.jsx │ ├── manual_initialization.html │ ├── manual_initialization.jsx │ ├── two_way_binding.html │ └── two_way_binding.jsx └── webpack.config.js ├── deploy_sdk.sh ├── docker-compose.yml.template ├── lib ├── FroalaEditor.jsx ├── FroalaEditorA.jsx ├── FroalaEditorButton.jsx ├── FroalaEditorFunctionality.jsx ├── FroalaEditorImg.jsx ├── FroalaEditorInput.jsx ├── FroalaEditorView.jsx ├── copy_bundles.sh └── index.d.ts ├── package.json ├── push-image-to-nexus.sh ├── version.json └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_STORE 2 | bower_components 3 | node_modules 4 | dist 5 | modules 6 | demo/dist/* 7 | package-lock.json -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .travis.yml 2 | deploy_sdk.sh 3 | docker-compose.yml.template 4 | Dockerfile 5 | push-image-to-nexus.sh 6 | version.json 7 | dist -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | git: 2 | depth: false 3 | quiet: false 4 | language: generic 5 | dist: bionic 6 | sudo: required 7 | branches: 8 | only: 9 | - /dev*/ 10 | - /AO-dev*/ 11 | - /QA*/ 12 | - /QE*/ 13 | - /RC*/ 14 | - /Release-Master*/ 15 | env: 16 | - SHORT_COMMIT= `git rev-parse --short=7 ${TRAVIS_COMMIT}` 17 | before_install: 18 | - echo $TRAVIS_BRANCH 19 | - echo $PWD 20 | - echo $TRAVIS_COMMIT 21 | - echo $BUILD_REPO_NAME 22 | jobs: 23 | include: 24 | if: commit_message =~ /(deploy-yes)/ 25 | script: 26 | - chmod u+x push-image-to-nexus.sh && bash push-image-to-nexus.sh 27 | - chmod u+x deploy_sdk.sh && bash deploy_sdk.sh 28 | notifications: 29 | email: 30 | recipients: 31 | - harasunu.narayan@froala.com 32 | on_success: always 33 | on_failure: always -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18 2 | 3 | LABEL maintainer="froala_git_travis_bot@idera.com" 4 | 5 | ARG PackageName 6 | ARG PackageVersion 7 | ARG NexusUser 8 | ARG NexusPassword 9 | 10 | WORKDIR /app/ 11 | COPY . . 12 | 13 | RUN apt update -y \ 14 | && apt install -y jq unzip wget 15 | RUN wget --no-check-certificate --user ${NexusUser} --password ${NexusPassword} https://nexus.tools.froala-infra.com/repository/Froala-npm/${PackageName}/-/${PackageName}-${PackageVersion}.tgz 16 | RUN npm install 17 | RUN npm run build 18 | 19 | EXPOSE 4000 20 | CMD ["npm","run","demo"] -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # React JS Froala WYSIWYG Editor 2 | 3 | [![npm](https://img.shields.io/npm/v/react-froala-wysiwyg.svg)](https://www.npmjs.com/package/react-froala-wysiwyg) 4 | [![npm](https://img.shields.io/npm/dm/react-froala-wysiwyg.svg)](https://www.npmjs.com/package/react-froala-wysiwyg) 5 | [![npm](https://img.shields.io/npm/l/react-froala-wysiwyg.svg)](https://www.npmjs.com/package/react-froala-wysiwyg) 6 | 7 | >react-froala-wyswiyg provides React bindings to the Froala WYSIWYG editor VERSION 3. 8 | 9 | ## Installation 10 | 11 | ```bash 12 | npm install react-froala-wysiwyg --save 13 | ``` 14 | 15 | ## Update editor version 16 | ```bash 17 | npm update froala-editor 18 | ``` 19 | ## Install font-awesome 20 | ```bash 21 | npm install font-awesome --save 22 | ``` 23 | 24 | ## Usage with Class Component 25 | 26 | #### 1. Require and use Froala Editor component inside your application. 27 | 28 | ```jsx 29 | import React from 'react'; 30 | import ReactDOM from 'react-dom/client'; 31 | 32 | 33 | // Require Editor CSS files. 34 | import 'froala-editor/css/froala_style.min.css'; 35 | import 'froala-editor/css/froala_editor.pkgd.min.css'; 36 | 37 | import FroalaEditorComponent from 'react-froala-wysiwyg'; 38 | 39 | // Import all Froala Editor plugins; 40 | // import 'froala-editor/js/plugins.pkgd.min.js'; 41 | 42 | // Import a single Froala Editor plugin. 43 | // import 'froala-editor/js/plugins/align.min.js'; 44 | 45 | // Import a language file. 46 | // import 'froala-editor/js/languages/de.js'; 47 | 48 | // Import a third-party plugin. 49 | // import 'froala-editor/js/third_party/image_tui.min.js'; 50 | // import 'froala-editor/js/third_party/embedly.min.js'; 51 | // import 'froala-editor/js/third_party/spell_checker.min.js'; 52 | 53 | // Include font-awesome css if required. 54 | // install using "npm install font-awesome --save" 55 | // import 'font-awesome/css/font-awesome.css'; 56 | // import 'froala-editor/js/third_party/font_awesome.min.js'; 57 | 58 | // Include special components if required. 59 | // import FroalaEditorView from 'react-froala-wysiwyg/FroalaEditorView'; 60 | // import FroalaEditorA from 'react-froala-wysiwyg/FroalaEditorA'; 61 | // import FroalaEditorButton from 'react-froala-wysiwyg/FroalaEditorButton'; 62 | // import FroalaEditorImg from 'react-froala-wysiwyg/FroalaEditorImg'; 63 | // import FroalaEditorInput from 'react-froala-wysiwyg/FroalaEditorInput'; 64 | 65 | // Render Froala Editor component. 66 | const root = ReactDOM.createRoot(document.getElementById('editor')); 67 | root.render(); 68 | ``` 69 | 70 | #### Add editor to UI by passing id to html element 71 | 72 | ``` 73 |
74 |
75 | ``` 76 | #### Pass properties to the wrapping DOM element 77 | 78 | ```js 79 | 85 | ``` 86 | 87 | **tag** attr is used to tell on which tag the editor is initialized. 88 | 89 | There are special tags: **a**, **button**, **img**, **input**. Do not use them in FroalaEditor component. To initialize the editor on a special tag, use `FroalaEditorA`, `FroalaEditorButton`, `FroalaEditorImg` and `FroalaEditorInput` components. 90 | 91 | 92 | ### Config 93 | 94 | You can pass editor options as component attribute (optional). 95 | 96 | `config={this.config}` 97 | 98 | You can pass any existing Froala option. Consult the [Froala documentation](https://www.froala.com/wysiwyg-editor/docs/options) to view the list of all the available options: 99 | 100 | ```js 101 | config={{ 102 | placeholderText: 'Edit Your Content Here!', 103 | charCounterCount: false 104 | }} 105 | ``` 106 | 107 | Aditional option is used: 108 | * **immediateReactModelUpdate**: (default: false) This option updates the React model as soon as a key is released in the editor. Note that it may affect performances. 109 | 110 | ### Events and Methods 111 | 112 | Events can be passed in with the options, with a key events and object where the key is the event name and the value is the callback function. 113 | 114 | ```js 115 | config={{ 116 | placeholder: "Edit Me", 117 | events : { 118 | 'focus' : function(e, editor) { 119 | console.log(editor.selection.get()); 120 | } 121 | } 122 | }} 123 | ``` 124 | 125 | Using the editor instance from the arguments of the callback you can call editor methods as described in the [method docs](http://froala.com/wysiwyg-editor/docs/methods). 126 | 127 | Froala events are described in the [events docs](https://froala.com/wysiwyg-editor/docs/events). 128 | 129 | 130 | ### Model 131 | 132 | The WYSIWYG HTML editor content model. 133 | 134 | `model = {this.state.model}` 135 | 136 | Two way binding: 137 | 138 | ```jsx 139 | import React from 'react'; 140 | 141 | class EditorComponent extends React.Component { 142 | constructor () { 143 | super(); 144 | 145 | this.handleModelChange = this.handleModelChange.bind(this); 146 | 147 | this.state = { 148 | model: 'Example text' 149 | }; 150 | } 151 | 152 | handleModelChange: function(model) { 153 | this.setState({ 154 | model: model 155 | }); 156 | } 157 | 158 | render () { 159 | return 163 | } 164 | } 165 | ``` 166 | 167 | To achieve one way binding and pass only the initial editor content, simply do not pass `onModelChange` attribute. 168 | 169 | Use the content in other places: 170 | 171 | ```js 172 | 173 | ``` 174 | 175 | ### Special tags 176 | You can also use the editor on **img**, **button**, **input** and **a** tags: 177 | 178 | ```js 179 | 182 | 185 | 188 | 191 | ``` 192 | 193 | The model must be an object containing the attributes for your special tags. Example: 194 | 195 | ```js 196 | constructor () { 197 | super(); 198 | 199 | this.handleModelChange = this.handleModelChange.bind(this); 200 | 201 | this.state = { 202 | model: {src: 'path/to/image.jpg'} 203 | }; 204 | } 205 | ``` 206 | 207 | * The model can contain a special attribute named **innerHTML** which inserts innerHTML in the element: If you are using 'button' tag, you can specify the button text like this: 208 | 209 | ```js 210 | this.state = { 211 | model: {innerHTML: 'Click Me'} 212 | }; 213 | ``` 214 | As the button text is modified by the editor, the **innerHTML** attribute from buttonModel model will be modified too. 215 | 216 | ## Manual Instantiation 217 | 218 | Gets the functionality to operate on the editor: create, destroy and get editor instance. Use it if you want to manually initialize the editor. 219 | 220 | `onManualControllerReady={this.handleManualController}` 221 | 222 | ```js 223 | handleManualController: function(initControls) { 224 | //... 225 | } 226 | ``` 227 | The object received by the function will contain the following methods: 228 | 229 | - **initialize**: Call this method to initialize the Froala Editor 230 | - **destroy**: Call this method to destroy the Froala Editor 231 | - **getEditor**: Call this method to retrieve the editor that was created. This method will return *null* if the editor was not yet created 232 | 233 | ## Displaying HTML 234 | 235 | To display content created with the froala editor use the `FroalaEditorView` component. 236 | 237 | ```js 238 | 242 | 245 | ``` 246 | 247 | 248 | ## Usage with Functional Component 249 | 250 | #### 1. Require and use Froala Editor component inside your application. 251 | 252 | ```jsx 253 | import React from 'react'; 254 | import ReactDOM from 'react-dom/client'; 255 | 256 | // Require Editor CSS files. 257 | import 'froala-editor/css/froala_style.min.css'; 258 | import 'froala-editor/css/froala_editor.pkgd.min.css'; 259 | 260 | import FroalaEditorComponent from 'react-froala-wysiwyg'; 261 | 262 | // Import all Froala Editor plugins; 263 | // import 'froala-editor/js/plugins.pkgd.min.js'; 264 | 265 | // Import a single Froala Editor plugin. 266 | // import 'froala-editor/js/plugins/align.min.js'; 267 | 268 | // Import a language file. 269 | // import 'froala-editor/js/languages/de.js'; 270 | 271 | // Import a third-party plugin. 272 | // import 'froala-editor/js/third_party/image_tui.min.js'; 273 | // import 'froala-editor/js/third_party/embedly.min.js'; 274 | // import 'froala-editor/js/third_party/spell_checker.min.js'; 275 | 276 | // Include font-awesome css if required. 277 | // install using "npm install font-awesome --save" 278 | // import 'font-awesome/css/font-awesome.css'; 279 | // import 'froala-editor/js/third_party/font_awesome.min.js'; 280 | 281 | // Include special components if required. 282 | // import FroalaEditorView from 'react-froala-wysiwyg/FroalaEditorView'; 283 | // import FroalaEditorA from 'react-froala-wysiwyg/FroalaEditorA'; 284 | // import FroalaEditorButton from 'react-froala-wysiwyg/FroalaEditorButton'; 285 | // import FroalaEditorImg from 'react-froala-wysiwyg/FroalaEditorImg'; 286 | // import FroalaEditorInput from 'react-froala-wysiwyg/FroalaEditorInput'; 287 | 288 | // Render Froala Editor component. 289 | const root = ReactDOM.createRoot(document.getElementById('editor')); 290 | root.render( 291 | 292 | ) 293 | 294 | ``` 295 | #### Add editor to UI by passing id to html element 296 | 297 | ``` 298 |
299 |
300 | ``` 301 | 302 | #### Pass properties to the wrapping DOM element 303 | 304 | ```js 305 | 311 | ``` 312 | **tag** attr is used to tell on which tag the editor is initialized. 313 | 314 | There are special tags: **a**, **button**, **img**, **input**. Do not use them in FroalaEditor component. To initialize the editor on a special tag, use `FroalaEditorA`, `FroalaEditorButton`, `FroalaEditorImg` and `FroalaEditorInput` components. 315 | 316 | 317 | ### Config 318 | 319 | You can pass editor options as component attribute (optional). 320 | 321 | `config={config}` 322 | 323 | You can pass any existing Froala option. Consult the [Froala documentation](https://www.froala.com/wysiwyg-editor/docs/options) to view the list of all the available options: 324 | 325 | ```js 326 | config={{ 327 | placeholderText: 'Edit Your Content Here!', 328 | charCounterCount: false 329 | }} 330 | ``` 331 | 332 | Aditional option is used: 333 | * **immediateReactModelUpdate**: (default: false) This option updates the React model as soon as a key is released in the editor. Note that it may affect performances. 334 | 335 | ### Events and Methods 336 | 337 | Events can be passed in with the options, with a key events and object where the key is the event name and the value is the callback function. 338 | 339 | ```js 340 | config={{ 341 | placeholder: "Edit Me", 342 | events : { 343 | 'focus' : function(e, editor) { 344 | console.log(editor.selection.get()); 345 | } 346 | } 347 | }} 348 | ``` 349 | 350 | Using the editor instance from the arguments of the callback you can call editor methods as described in the [method docs](http://froala.com/wysiwyg-editor/docs/methods). 351 | 352 | Froala events are described in the [events docs](https://froala.com/wysiwyg-editor/docs/events). 353 | 354 | Now you can use these buttons in options: 355 | ```javascript 356 | toolbarButtons: [['undo', 'redo' , 'bold'], ['alert', 'clear', 'insert']], 357 | 358 | ``` 359 | 360 | ### Model 361 | 362 | The WYSIWYG HTML editor content model. 363 | 364 | `model = {model}` 365 | 366 | Two way binding: 367 | 368 | ```jsx 369 | import React,{ useState } from 'react'; 370 | 371 | const App=()=> { 372 | const [model,setModel] = useState("Example Set"); 373 | 374 | const handleModelChange= (event)=>{ 375 | setModel(event) 376 | } 377 | return ( 378 |
379 | 383 | 386 |
387 | ); 388 | } 389 | ``` 390 | 391 | To achieve one way binding and pass only the initial editor content, simply do not pass `onModelChange` attribute. 392 | 393 | Use the content in other places: 394 | 395 | ```js 396 | 397 | ``` 398 | 399 | ### Special tags 400 | You can also use the editor on **img**, **button**, **input** and **a** tags: 401 | 402 | ```js 403 | 406 | 409 | 412 | 415 | 416 | ``` 417 | The model must be an object containing the attributes for your special tags. Example: 418 | 419 | ```js 420 | model={{src: 'path/to/image.jpg', 421 | width:"300px", 422 | alt:"Old Clock" 423 | }} 424 | ``` 425 | 426 | * The model can contain a special attribute named **innerHTML** which inserts innerHTML in the element: If you are using 'button' tag, you can specify the button text like this: 427 | 428 | ```js 429 | model={{innerHTML: 'Click Me'}} 430 | ``` 431 | As the button text is modified by the editor, the **innerHTML** attribute from buttonModel model will be modified too. 432 | 433 | ## Usage with Server-Side Rendering 434 | 435 | When using Server-Side Rendering you can use Froala components as described previously. If you require additonal plugins or translations, you will have to use React lazy loading 436 | in order to load the plugins first. Please refer to the examples below. 437 | 438 | ### Class component 439 | 440 | ```js 441 | import React, { Suspense } from "react"; 442 | 443 | import 'froala-editor/css/froala_editor.pkgd.min.css'; 444 | import 'froala-editor/css/froala_style.css'; 445 | 446 | const FroalaEditor = React.lazy(() => import('react-froala-wysiwyg')); 447 | 448 | export default class MyComponent extends React.Component { 449 | constructor () { 450 | super(); 451 | this.state = { 452 | isInitialized: false 453 | }; 454 | } 455 | 456 | componentDidMount() { 457 | // Import all Froala Editor plugins; 458 | import('froala-editor/js/plugins.pkgd.min.js').then(() => this.setState({isInitialized: true})); 459 | } 460 | 461 | render() { 462 | return <> 463 | {this.state.isInitialized && ( 464 | Loading...

}> 465 | 468 |
469 | )} 470 | ; 471 | } 472 | } 473 | ``` 474 | 475 | ### Functional component 476 | 477 | ```js 478 | import React, { Suspense, useEffect, useState } from "react"; 479 | 480 | import 'froala-editor/css/froala_editor.pkgd.min.css'; 481 | import 'froala-editor/css/froala_style.css'; 482 | 483 | const FroalaEditor = React.lazy(() => import('react-froala-wysiwyg')); 484 | 485 | export default function MyComponent() { 486 | const [isInitialized, setIsInitialized] = useState(false); 487 | 488 | useEffect(() => { 489 | async function initPlugins() { 490 | // Import all Froala Editor plugins; 491 | await import('froala-editor/js/plugins.pkgd.min.js'); 492 | setIsInitialized(true); 493 | } 494 | if (!isInitialized) { 495 | initPlugins(); 496 | } 497 | }); 498 | 499 | return <> 500 | {isInitialized && ( 501 | Loading...

}> 502 | 505 |
506 | )} 507 | ; 508 | } 509 | ``` 510 | 511 | ## Manual Instantiation 512 | 513 | Gets the functionality to operate on the editor: create, destroy and get editor instance. Use it if you want to manually initialize the editor. 514 | 515 | `onManualControllerReady={handleManualController}` 516 | 517 | ```js 518 | handleManualController =(initControls) =>{ 519 | //... 520 | } 521 | ``` 522 | The object received by the function will contain the following methods: 523 | 524 | - **initialize**: Call this method to initialize the Froala Editor 525 | - **destroy**: Call this method to destroy the Froala Editor 526 | - **getEditor**: Call this method to retrieve the editor that was created. This method will return *null* if the editor was not yet created 527 | 528 | ## Displaying HTML 529 | 530 | To display content created with the froala editor use the `FroalaEditorView` component. 531 | 532 | ```js 533 | 537 | 540 | ``` 541 | 542 | #### Specific option for special tags 543 | 544 | * **reactIgnoreAttrs**: (default: null) This option is an array of attributes that you want to ignore when the editor updates the froalaModel: 545 | 546 | ```js 547 | config: { 548 | reactIgnoreAttrs: ['class', 'id'] 549 | }, 550 | ``` 551 | 552 | ## Using type definition file 553 | `index.d.ts` file is the type definition file for this repository. It is placed inside lib folder.In order to use it in your code , use the following line: 554 | ``` 555 | /// 556 | ``` 557 | where path is the location of index.d.ts file. 558 | 559 | 560 | ### Custom Buttons 561 | 562 | You can pass the custom buttons to the editor by following way: 563 | 564 | ```javascript 565 | 601 | 602 | ``` 603 | Now you can use these buttons in options: 604 | ```javascript 605 | toolbarButtons: [['undo', 'redo' , 'bold'], ['alert', 'clear', 'insert']], 606 | 607 | ``` 608 | 609 | ## License 610 | 611 | The `react-froala-wyswiyg` project is under MIT license. However, in order to use Froala WYSIWYG HTML Editor plugin you should purchase a license for it. 612 | 613 | Froala Editor has [3 different licenses](http://froala.com/wysiwyg-editor/pricing) for commercial use. 614 | For details please see [License Agreement](http://froala.com/wysiwyg-editor/terms). 615 | 616 | ## Development environment setup 617 | 618 | If you want to contribute to react-froala-wyswiyg, you will first need to install the required tools to get the project going. 619 | 620 | #### Prerequisites 621 | 622 | * [Node Package Manager](https://npmjs.org/) (NPM) 623 | * [Git](http://git-scm.com/) 624 | 625 | #### Install dependencies 626 | 627 | $ npm install 628 | 629 | #### Build 630 | 631 | $ npm run build 632 | 633 | #### Run Demo 634 | 635 | $ npm run demo 636 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /configs/bs-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | server: { 3 | middleware: { 4 | // overrides the second middleware default with new settings 5 | 1: require('connect-history-api-fallback')({index: '/src/index.html', verbose: true}) 6 | } 7 | } 8 | }; -------------------------------------------------------------------------------- /demo/src/basic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello React 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/src/basic.jsx: -------------------------------------------------------------------------------- 1 | // Note that Froala Editor has to be required separately. 2 | 3 | import 'froala-editor/css/froala_editor.pkgd.min.css'; 4 | import 'froala-editor/css/froala_style.css'; 5 | import 'file-loader?name=[name].[ext]!./basic.html'; 6 | 7 | import FroalaEditor from 'react-froala-wysiwyg'; 8 | import React from 'react'; 9 | import ReactDOM from 'react-dom/client'; 10 | 11 | // Render Froala Editor component. 12 | const root = ReactDOM.createRoot(document.getElementById('editor')); 13 | root.render(); 23 | 24 | -------------------------------------------------------------------------------- /demo/src/custombutton.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello React 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/src/custombutton.jsx: -------------------------------------------------------------------------------- 1 | import 'froala-editor/css/froala_editor.pkgd.min.css'; 2 | import 'froala-editor/css/froala_style.css'; 3 | import 'file-loader?name=[name].[ext]!./custombutton.html'; 4 | 5 | import FroalaEditor from 'react-froala-wysiwyg'; 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom/client'; 8 | import Froalaeditor from 'froala-editor'; 9 | Froalaeditor.DefineIcon('alert', {NAME: 'info', SVG_KEY: 'help'}); 10 | Froalaeditor.RegisterCommand('alert', { 11 | title: 'Hello', 12 | focus: false, 13 | undo: false, 14 | refreshAfterCallback: false, 15 | callback: function () { 16 | alert('Hello!'); 17 | } 18 | }); 19 | 20 | Froalaeditor.DefineIcon('clear', {NAME: 'remove', SVG_KEY: 'remove'}); 21 | Froalaeditor.RegisterCommand('clear', { 22 | title: 'Clear HTML', 23 | focus: false, 24 | undo: true, 25 | refreshAfterCallback: true, 26 | callback: function () { 27 | this.html.set(''); 28 | this.events.focus(); 29 | } 30 | }); 31 | 32 | Froalaeditor.DefineIcon('insert', {NAME: 'plus', SVG_KEY: 'add'}); 33 | Froalaeditor.RegisterCommand('insert', { 34 | title: 'Insert HTML', 35 | focus: true, 36 | undo: true, 37 | refreshAfterCallback: true, 38 | callback: function () { 39 | this.html.insert('My New HTML'); 40 | } 41 | }); 42 | 43 | // Render Froala Editor component. 44 | const root = ReactDOM.createRoot(document.getElementById('editor')); 45 | root.render(); -------------------------------------------------------------------------------- /demo/src/edit_inline.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello React 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/src/edit_inline.jsx: -------------------------------------------------------------------------------- 1 | // Note that Froala Editor has to be required separately 2 | import 'froala-editor/css/froala_editor.pkgd.min.css'; 3 | import 'froala-editor/css/froala_style.css'; 4 | 5 | import FroalaEditor from 'react-froala-wysiwyg'; 6 | import React from 'react'; 7 | import ReactDOM from 'react-dom/client'; 8 | 9 | // Render Froala Editor component. 10 | class EditorComponent extends React.Component { 11 | constructor() { 12 | super(); 13 | 14 | this.config = { 15 | placeholderText: 'Add a Title', 16 | charCounterCount: false, 17 | toolbarInline: true, 18 | events: { 19 | initialized: function() { 20 | console.log('initialized'); 21 | } 22 | } 23 | }; 24 | 25 | this.state = { 26 | myTitle: 'Click here to edit this text.' 27 | }; 28 | 29 | this.handleModelChange = this.handleModelChange.bind(this); 30 | this.handleInputChange = this.handleInputChange.bind(this); 31 | } 32 | 33 | handleModelChange (model) { 34 | this.setState({ 35 | myTitle: model 36 | }) 37 | } 38 | 39 | handleInputChange (e) { 40 | this.setState({ 41 | myTitle: e.target.value 42 | }) 43 | } 44 | 45 | render () { 46 | return( 47 |
48 |

Inline Edit

49 | 55 | 56 |
57 | ); 58 | } 59 | } 60 | 61 | const root = ReactDOM.createRoot(document.getElementById('editor')); 62 | root.render(); 63 | 64 | require("file-loader?name=[name].[ext]!./edit_inline.html"); -------------------------------------------------------------------------------- /demo/src/full_editor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello React 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/src/full_editor.jsx: -------------------------------------------------------------------------------- 1 | // Note that Froala Editor has to be required separately 2 | import 'froala-editor/css/froala_editor.pkgd.min.css'; 3 | import 'froala-editor/css/froala_style.css'; 4 | import 'file-loader?name=[name].[ext]!./full_editor.html'; 5 | import 'froala-editor/js/plugins.pkgd.min.js'; 6 | 7 | import FroalaEditor from 'react-froala-wysiwyg'; 8 | import FroalaEditorView from 'react-froala-wysiwyg/FroalaEditorView'; 9 | import React from 'react'; 10 | import ReactDOM from 'react-dom/client'; 11 | 12 | // Render Froala Editor component. 13 | class EditorComponent extends React.Component { 14 | constructor() { 15 | super(); 16 | 17 | this.state = { 18 | content: 'My Document\'s Title' 19 | }; 20 | 21 | this.handleModelChange = this.handleModelChange.bind(this); 22 | } 23 | 24 | handleModelChange (model) { 25 | this.setState({ 26 | content: model 27 | }); 28 | } 29 | 30 | render () { 31 | return( 32 |
33 |

Full Featured

34 | 38 |

Rendered Content:

39 | 42 |
43 | ); 44 | } 45 | 46 | } 47 | 48 | const root = ReactDOM.createRoot(document.getElementById('editor')); 49 | root.render(); 50 | 51 | -------------------------------------------------------------------------------- /demo/src/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/froala/react-froala-wysiwyg/8c7465091bc5a8b134edd3d5b2aacbe813ed292b/demo/src/image.jpg -------------------------------------------------------------------------------- /demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello React 6 | 7 | 8 | 20 | 21 | -------------------------------------------------------------------------------- /demo/src/init_on_button.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello React 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/src/init_on_button.jsx: -------------------------------------------------------------------------------- 1 | // Note that Froala Editor has to be required separately 2 | import 'froala-editor/css/froala_editor.pkgd.min.css'; 3 | import 'froala-editor/css/froala_style.css'; 4 | import 'file-loader?name=[name].[ext]!./init_on_button.html'; 5 | import 'froala-editor/js/plugins.pkgd.min.js'; 6 | import FroalaEditorButton from 'react-froala-wysiwyg/FroalaEditorButton'; 7 | import React from 'react'; 8 | import ReactDOM from 'react-dom/client'; 9 | 10 | // Render Froala Editor component. 11 | class EditorComponent extends React.Component { 12 | constructor() { 13 | super(); 14 | 15 | this.state = { 16 | content: { 17 | innerHTML: 'Click Me' 18 | } 19 | }; 20 | 21 | this.handleModelChange = this.handleModelChange.bind(this); 22 | } 23 | 24 | handleModelChange (model) { 25 | this.setState({ 26 | content: model 27 | }); 28 | } 29 | 30 | render () { 31 | return( 32 |
33 |

Editor on 'button' tag

34 | 38 | 39 |

Model Obj:

40 |
{JSON.stringify(this.state.content)}
41 |
42 | ); 43 | } 44 | } 45 | 46 | const root = ReactDOM.createRoot(document.getElementById('editor')); 47 | root.render(); 48 | 49 | -------------------------------------------------------------------------------- /demo/src/init_on_image.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello React 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/src/init_on_image.jsx: -------------------------------------------------------------------------------- 1 | // Note that Froala Editor has to be required separately 2 | 3 | import 'froala-editor/css/froala_editor.pkgd.min.css'; 4 | import 'froala-editor/css/froala_style.css'; 5 | import 'froala-editor/js/plugins.pkgd.min.js'; 6 | import 'file-loader?name=[name].[ext]!./init_on_image.html'; 7 | 8 | 9 | import FroalaEditorImg from 'react-froala-wysiwyg/FroalaEditorImg'; 10 | import React from 'react'; 11 | import ReactDOM from 'react-dom/client'; 12 | 13 | // Render Froala Editor component. 14 | class EditorComponent extends React.Component { 15 | constructor () { 16 | super(); 17 | 18 | this.config = { 19 | reactIgnoreAttrs: ['tmpattr'] 20 | } 21 | 22 | this.handleModelChange = this.handleModelChange.bind(this); 23 | 24 | this.state = { 25 | content: { 26 | src: './image.jpg', 27 | id: 'froalaEditor', 28 | tmpattr: 'This attribute will be ignored on change.' 29 | } 30 | }; 31 | } 32 | 33 | handleModelChange (model) { 34 | this.setState({content: model}); 35 | } 36 | 37 | render () { 38 | return( 39 |
40 |

Editor on 'img' tag. Two way binding.

41 |    46 | 47 | 52 | 53 |

Model Obj:

54 |
{JSON.stringify(this.state.content)}
55 |
56 | ); 57 | } 58 | } 59 | 60 | const root = ReactDOM.createRoot(document.getElementById('editor')); 61 | root.render(); 62 | 63 | -------------------------------------------------------------------------------- /demo/src/init_on_input.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello React 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/src/init_on_input.jsx: -------------------------------------------------------------------------------- 1 | // Note that Froala Editor has to be required separately 2 | import 'froala-editor/css/froala_editor.pkgd.min.css'; 3 | import 'froala-editor/js/plugins.pkgd.min.js'; 4 | import 'froala-editor/css/froala_style.css'; 5 | import 'file-loader?name=[name].[ext]!./init_on_input.html'; 6 | 7 | 8 | import FroalaEditorInput from 'react-froala-wysiwyg/FroalaEditorInput'; 9 | import React from 'react'; 10 | import ReactDOM from 'react-dom/client'; 11 | 12 | // Render Froala Editor component. 13 | class EditorComponent extends React.Component { 14 | constructor() { 15 | super(); 16 | 17 | this.state = { 18 | content: { 19 | placeholder: 'I am an input!' 20 | } 21 | }; 22 | 23 | this.handleModelChange = this.handleModelChange.bind(this); 24 | } 25 | 26 | handleModelChange (model) { 27 | this.setState({ 28 | content: model 29 | }); 30 | } 31 | 32 | render () { 33 | return( 34 |
35 |

Sample 7: Editor on 'input' tag

36 | 40 |

Model Obj:

41 |
{JSON.stringify(this.state.content)}
42 |
43 | ); 44 | } 45 | } 46 | 47 | const root = ReactDOM.createRoot(document.getElementById('editor')); 48 | root.render(); 49 | 50 | -------------------------------------------------------------------------------- /demo/src/init_on_link.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello React 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/src/init_on_link.jsx: -------------------------------------------------------------------------------- 1 | // Note that Froala Editor has to be required separately 2 | 3 | import 'froala-editor/css/froala_editor.pkgd.min.css'; 4 | import 'froala-editor/css/froala_style.css'; 5 | import 'froala-editor/js/plugins.pkgd.min.js'; 6 | 7 | 8 | import FroalaEditorA from 'react-froala-wysiwyg/FroalaEditorA'; 9 | import React from 'react'; 10 | import ReactDOM from 'react-dom/client'; 11 | 12 | // Render Froala Editor component. 13 | class EditorComponent extends React.Component { 14 | constructor() { 15 | super(); 16 | 17 | this.state = { 18 | content: { 19 | href: 'https://www.froala.com/wysiwyg-editor' 20 | }, 21 | initControls: null 22 | }; 23 | 24 | this.handleModelChange = this.handleModelChange.bind(this); 25 | } 26 | 27 | handleModelChange (model) { 28 | this.setState({ 29 | content: model 30 | }); 31 | } 32 | 33 | render () { 34 | return( 35 |
36 |

Editor on 'a' tag.

37 |
38 | 42 | Froala 43 | 44 |
45 |
46 | ); 47 | } 48 | } 49 | 50 | const root = ReactDOM.createRoot(document.getElementById('editor')); 51 | root.render(); 52 | 53 | import "file-loader?name=[name].[ext]!./init_on_link.html"; -------------------------------------------------------------------------------- /demo/src/manual_initialization.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello React 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/src/manual_initialization.jsx: -------------------------------------------------------------------------------- 1 | // Note that Froala Editor has to be required separately 2 | import 'froala-editor/css/froala_editor.pkgd.min.css'; 3 | import 'froala-editor/css/froala_style.css'; 4 | import 'file-loader?name=[name].[ext]!./manual_initialization.html'; 5 | 6 | import FroalaEditor from 'react-froala-wysiwyg'; 7 | import FroalaEditorView from 'react-froala-wysiwyg/FroalaEditorView'; 8 | import React from 'react'; 9 | import ReactDOM from 'react-dom/client'; 10 | 11 | // Render Froala Editor component. 12 | class EditorComponent extends React.Component { 13 | constructor () { 14 | super(); 15 | 16 | this.state = { 17 | initControls: null 18 | }; 19 | 20 | this.handleModelChange = this.handleModelChange.bind(this); 21 | this.handleController = this.handleController.bind(this); 22 | this.initializeEditor = this.initializeEditor.bind(this); 23 | this.destroyEditor = this.destroyEditor.bind(this); 24 | this.deleteAll = this.deleteAll.bind(this); 25 | } 26 | 27 | handleModelChange (model) { 28 | this.setState({content: model}); 29 | } 30 | 31 | deleteAll () { 32 | if (!this.state.initControls) { 33 | return; 34 | } 35 | this.state.initControls.getEditor().html.set(); 36 | this.state.initControls.getEditor().undo.reset(); 37 | this.state.initControls.getEditor().undo.saveStep(); 38 | } 39 | 40 | handleController (initControls) { 41 | this.setState({initControls: initControls}); 42 | } 43 | 44 | initializeEditor () { 45 | this.state.initControls.initialize(); 46 | this.setState({initControls: this.state.initControls}); 47 | } 48 | 49 | destroyEditor () { 50 | this.state.initControls.destroy(); 51 | this.setState({initControls: this.state.initControls}); 52 | } 53 | 54 | render () { 55 | return( 56 |
57 |

Manual Initialization

58 | {this.state.initControls ? 59 | 60 | : 61 | null 62 | } 63 | {this.state.initControls && this.state.initControls.getEditor() ? 64 | 65 | 66 | 67 | 68 | : 69 | null 70 | } 71 | 76 | Check out the Froala Editor 77 | 78 |
79 | ); 80 | } 81 | } 82 | 83 | const root = ReactDOM.createRoot(document.getElementById('editor')); 84 | root.render(); 85 | 86 | -------------------------------------------------------------------------------- /demo/src/two_way_binding.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello React 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/src/two_way_binding.jsx: -------------------------------------------------------------------------------- 1 | // Note that Froala Editor has to be required separately 2 | 3 | import 'froala-editor/css/froala_editor.pkgd.min.css'; 4 | import 'froala-editor/css/froala_style.css' 5 | import 'file-loader?name=[name].[ext]!./two_way_binding.html'; 6 | 7 | import FroalaEditor from 'react-froala-wysiwyg'; 8 | import React from 'react'; 9 | import ReactDOM from 'react-dom/client'; 10 | 11 | // Render Froala Editor component. 12 | class EditorComponent extends React.Component { 13 | constructor () { 14 | super(); 15 | 16 | this.state = { 17 | content: 'Type here and the other input should update.' 18 | }; 19 | 20 | this.handleModelChange = this.handleModelChange.bind(this); 21 | } 22 | 23 | handleModelChange (model) { 24 | this.setState({ 25 | content: model 26 | }); 27 | } 28 | 29 | render () { 30 | return( 31 |
32 |

Two way binding

33 |
37 | 41 |
42 | ); 43 | } 44 | } 45 | 46 | const root = ReactDOM.createRoot(document.getElementById('editor')); 47 | root.render(); 48 | 49 | -------------------------------------------------------------------------------- /demo/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require("webpack"); 2 | var CopyWebpackPlugin = require('copy-webpack-plugin'); 3 | 4 | module.exports = { 5 | entry: { 6 | basic: './src/basic.jsx', 7 | edit_inline: './src/edit_inline.jsx', 8 | full_editor: './src/full_editor.jsx', 9 | two_way_binding: './src/two_way_binding.jsx', 10 | manual_initialization: './src/manual_initialization.jsx', 11 | init_on_image: './src/init_on_image.jsx', 12 | init_on_button: './src/init_on_button.jsx', 13 | init_on_link: './src/init_on_link.jsx', 14 | init_on_input: './src/init_on_input.jsx', 15 | custombutton: './src/custombutton.jsx' 16 | }, 17 | 18 | optimization: { 19 | concatenateModules: false 20 | }, 21 | 22 | module: { 23 | rules: [ 24 | { 25 | test: /\.jsx$/, 26 | use: { 27 | loader: 'babel-loader', 28 | options: { 29 | cacheDirectory: true, 30 | presets: [ 31 | ['@babel/preset-env', { 32 | 'targets': { 33 | "ie": "11" 34 | }, 35 | "corejs": 3, 36 | "useBuiltIns": "entry" 37 | }], 38 | '@babel/preset-react'] 39 | } 40 | } 41 | }, { 42 | test: /\.css$/, 43 | use: [ 44 | 'style-loader', 45 | { 46 | loader: 'css-loader', 47 | options: { 48 | esModule: false 49 | } 50 | } 51 | ] 52 | }, 53 | { 54 | test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, 55 | use: "url-loader?limit=10000&mimetype=application/font-woff" 56 | }, { 57 | test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, 58 | use: "url-loader?limit=10000&mimetype=application/font-woff" 59 | }, { 60 | test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, 61 | use: "url-loader?limit=10000&mimetype=application/octet-stream" 62 | }, { 63 | test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, 64 | use: "file-loader" 65 | }, { 66 | test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, 67 | use: "url-loader?limit=10000&mimetype=image/svg+xml" 68 | } 69 | ] 70 | }, 71 | 72 | resolve: { 73 | alias: { 74 | "react-froala-wysiwyg": '../../dist' 75 | }, 76 | modules: ['node_modules'] 77 | }, 78 | 79 | output: { 80 | path: __dirname + '/dist/', 81 | filename: '[name].js', 82 | publicPath: '/' 83 | }, 84 | 85 | plugins: [ 86 | new webpack.ProvidePlugin({ 87 | 88 | }), 89 | 90 | new CopyWebpackPlugin({ 91 | patterns: [ 92 | { from: './src/index.html' }, 93 | { from: './src/image.jpg' } 94 | ] 95 | }) 96 | ] 97 | }; -------------------------------------------------------------------------------- /deploy_sdk.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Steps 4 | # Identify the build agent. Check whether build agent is same as deployment server 5 | # Login to build server and build, run, check the new changes. 6 | # --force-recreate for docker-compose 7 | # --no-cache for docker build 8 | # -f for npm install 9 | # -v --rmi all for docker compose down 10 | 11 | if [ "${TRAVIS_PULL_REQUEST}" != "false" ]; then echo "Not deploying on a pull request !!!" && exit 0; fi 12 | 13 | # Define the global variables 14 | BRANCH_NAME=$(echo "${TRAVIS_BRANCH}" | tr '[:upper:]' '[:lower:]') 15 | PACKAGE_VERSION="$(jq '.version' version.json | tr -d '"')" 16 | IMAGE_NAME="$(echo "froala-${BUILD_REPO_NAME}_${TRAVIS_BRANCH}:${PACKAGE_VERSION}" | tr '[:upper:]' '[:lower:]')" 17 | BASE_DOMAIN="froala-infra.com" 18 | AO_IDENTIFIER="${TRAVIS_BRANCH}" 19 | BRANCH_LENGHT=$(echo "${TRAVIS_BRANCH}" |awk '{print length}') 20 | LW_REPO_NAME=$(echo "${BUILD_REPO_NAME}" | tr '[:upper:]' '[:lower:]' | sed -e 's/-//g' -e 's/\.//g' -e 's/_//g') 21 | CT_INDEX=0 22 | MAX_DEPLOYMENTS_NR=0 23 | SDK_ENVIRONMENT="" 24 | DEPLOYMENT_SERVER="" 25 | SERVICE_NAME="" 26 | CONTAINER_NAME="" 27 | OLDEST_CONTAINER="" 28 | 29 | # Copy the ssh key 30 | echo "${SSH_KEY}" | base64 --decode > /tmp/sshkey.pem 31 | chmod 400 /tmp/sshkey.pem 32 | 33 | # Select the deployment server based on the branch. 34 | case "${BRANCH_NAME}" in 35 | dev*) SDK_ENVIRONMENT="dev" && DEPLOYMENT_SERVER="${FROALA_SRV_DEV}";; 36 | ao-dev*) SDK_ENVIRONMENT="dev" && DEPLOYMENT_SERVER="${FROALA_SRV_DEV}";; 37 | qa*) SDK_ENVIRONMENT="qa" && DEPLOYMENT_SERVER="${FROALA_SRV_QA}";; 38 | qe*) SDK_ENVIRONMENT="qe" && DEPLOYMENT_SERVER="${FROALA_SRV_QE}";; 39 | rc*) SDK_ENVIRONMENT="stg" && DEPLOYMENT_SERVER="${FROALA_SRV_STAGING}";; 40 | release-master*) SDK_ENVIRONMENT="stg" && DEPLOYMENT_SERVER=${FROALA_SRV_STAGING};; 41 | ft*) echo "Building only on feature branch ${TRAVIS_BRANCH}... will not deploy..." && exit 0;; 42 | bf*) echo "Building only on bugfix branch ${TRAVIS_BRANCH}... will not deploy..." && exit 0;; 43 | *) echo "Not a deployment branch" && exit 1;; 44 | esac 45 | 46 | # Set the short branch name 47 | if [ "${BRANCH_LENGHT}" -lt 18 ]; then 48 | SHORT_TRAVIS_BRANCH="${TRAVIS_BRANCH}" 49 | else 50 | SHORT_TRAVIS_BRANCH="${TRAVIS_BRANCH:0:8}${TRAVIS_BRANCH: -8}" 51 | fi 52 | LW_SHORT_TRAVIS_BRANCH="$(echo "${SHORT_TRAVIS_BRANCH}" | sed -e 's/-//g' -e 's/\.//g' -e 's/_//g' | tr '[:upper:]' '[:lower:]')" 53 | 54 | # Get the maximum allowed deployment for given environment 55 | function max_allowed_deployment(){ 56 | echo "getting max deployments for environment ${SDK_ENVIRONMENT}" 57 | MAX_DEPLOYMENTS_NR=$(jq --arg sdkenvironment "${SDK_ENVIRONMENT}" '.[$sdkenvironment]' version.json | tr -d '"') 58 | echo "Max allowed deployments: ${MAX_DEPLOYMENTS_NR}" 59 | } 60 | max_allowed_deployment 61 | 62 | # Get the total numbers of deployed container for given environment 63 | function existing_deployments(){ 64 | echo "Checking the existing number of running container(s)" 65 | EXISTING_DEPLOYMENTS_NR=$(ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem "${SSH_USER}"@"${DEPLOYMENT_SERVER}" "sudo docker ps | grep -i ${LW_REPO_NAME}-${AO_IDENTIFIER}" | wc -l) 66 | echo "Number of existing deployment: ${EXISTING_DEPLOYMENTS_NR}" 67 | } 68 | existing_deployments 69 | 70 | # Get the old container name, no of deployments, and generate the new index and container name 71 | function generate_container_name(){ 72 | 73 | DEPL=$(ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem "${SSH_USER}"@"${DEPLOYMENT_SERVER}" sudo docker ps | grep -i "${LW_REPO_NAME}"-"${AO_IDENTIFIER}") 74 | echo "Containers running for ${AO_IDENTIFIER}: ${DEPL}" 75 | echo "${DEPL}" > file.txt 76 | 77 | echo "Getting indexes of oldest and latest deployed containers for ${AO_IDENTIFIER}" 78 | CT_LOWER_INDEX=$(awk -F'-' '{print $NF }' < file.txt | sort -nk1 | head -1) 79 | CT_HIGHER_INDEX=$(awk -F'-' '{print $NF }' < file.txt | sort -nk1 | tail -1) 80 | echo "Lowest index : ${CT_LOWER_INDEX} ; and Highest index : ${CT_HIGHER_INDEX}" 81 | 82 | if [ -z "${DEPL}" ]; then 83 | echo "First deployment. Setting the container name." 84 | CT_INDEX=1 85 | CONTAINER_NAME="${LW_REPO_NAME}-${AO_IDENTIFIER}-${CT_INDEX}" 86 | SERVICE_NAME="${LW_REPO_NAME}-${LW_SHORT_TRAVIS_BRANCH}" 87 | else 88 | echo "Multiple deployments detected. Setting the container name (old and new)" 89 | CT_INDEX=${CT_HIGHER_INDEX} && CT_INDEX=$((CT_INDEX+1)) 90 | OLDEST_CONTAINER="${LW_REPO_NAME}-${AO_IDENTIFIER}-${CT_LOWER_INDEX}" 91 | CONTAINER_NAME="${LW_REPO_NAME}-${AO_IDENTIFIER}-${CT_INDEX}" 92 | SERVICE_NAME="${LW_REPO_NAME}-${LW_SHORT_TRAVIS_BRANCH}-${CT_INDEX}" 93 | echo "New index: ${CT_INDEX}" 94 | fi 95 | } 96 | generate_container_name 97 | 98 | # Print useful details. 99 | echo -e "\n" 100 | echo "----------------------------------------------------------------------" 101 | echo " Selected environment: ${SDK_ENVIRONMENT}. " 102 | echo " Deployment server: ${DEPLOYMENT_SERVER}. " 103 | echo " Max allowed deployments: ${MAX_DEPLOYMENTS_NR}. " 104 | echo " Number of existing deployment: ${EXISTING_DEPLOYMENTS_NR} " 105 | echo " Oldest container name: ${OLDEST_CONTAINER} " 106 | echo " Container name for this deployment: ${CONTAINER_NAME} " 107 | echo "----------------------------------------------------------------------" 108 | echo -e "\n" 109 | 110 | # Set the deployment URL 111 | DEPLOYMENT_URL="${CONTAINER_NAME}.${SDK_ENVIRONMENT}.${BASE_DOMAIN}" 112 | 113 | # Modify the compose file and run the docker-compose. 114 | function deploy(){ 115 | 116 | # Copy the docker-compose template to docker-compose.yml 117 | cp docker-compose.yml.template docker-compose.yml 118 | 119 | # Replace the sample values 120 | sed -i "s/ImageName/${NEXUS_CR_TOOLS_URL}\/${IMAGE_NAME}/g" docker-compose.yml 121 | sed -i "s/UrlName/${DEPLOYMENT_URL}/g" docker-compose.yml 122 | sed -i "s/ServiceName/${SERVICE_NAME}/g" docker-compose.yml 123 | sed -i "s/PortNum/${CONTAINER_SERVICE_PORTNO}/g" docker-compose.yml 124 | sed -i "s/ContainerName/${CONTAINER_NAME}/g" docker-compose.yml 125 | 126 | echo -e "\n" 127 | echo "Below is the content of docker-compose.yml" 128 | echo "-------------------------------------------------" 129 | cat docker-compose.yml 130 | echo "-------------------------------------------------" 131 | echo -e "\n" 132 | 133 | # Remove the old docker-compose from deployment_server 134 | ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem "${SSH_USER}"@"${DEPLOYMENT_SERVER}" "if [ -d /services/${SERVICE_NAME} ]; then rm -rf /services/${SERVICE_NAME}; fi && mkdir /services/${SERVICE_NAME}" 135 | 136 | # Copy the latest docker-compose file to deployment_server 137 | scp -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem docker-compose.yml "${SSH_USER}"@"${DEPLOYMENT_SERVER}":/services/"${SERVICE_NAME}"/docker-compose.yml 138 | 139 | # Run docker-compose pull on deployment_server 140 | ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem "${SSH_USER}"@"${DEPLOYMENT_SERVER}" "cd /services/${SERVICE_NAME}/ && sudo docker-compose pull" 141 | sleep 10 142 | 143 | # Run docker-compose up on deployment_server 144 | ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem "${SSH_USER}"@"${DEPLOYMENT_SERVER}" "cd /services/${SERVICE_NAME}/ && sudo docker-compose up -d --force-recreate" 145 | sleep 370 146 | 147 | RET_CODE=$(curl -k -s -o /tmp/notimportant.txt -w "%{http_code}" https://"${DEPLOYMENT_URL}") 148 | echo "validation code: $RET_CODE for https://${DEPLOYMENT_URL}" 149 | if [ "${RET_CODE}" -ne 200 ]; then 150 | echo "Deployment validation failed!!! Please check pipeline logs." 151 | exit 1 152 | else 153 | echo -e "\n\tService available at URL: https://${DEPLOYMENT_URL}\n" 154 | fi 155 | } 156 | 157 | # If existing deployment less than max deployment then just deploy don't remove old container. 158 | if [ "${EXISTING_DEPLOYMENTS_NR}" -lt "${MAX_DEPLOYMENTS_NR}" ]; then 159 | deploy 160 | fi 161 | 162 | # If existing deployment equals max deployment then delete oldest container. 163 | if [ "${EXISTING_DEPLOYMENTS_NR}" -ge "${MAX_DEPLOYMENTS_NR}" ]; then 164 | 165 | echo "Maximum deployments reached on ${SDK_ENVIRONMENT} environment for ${BUILD_REPO_NAME}." 166 | echo "Stopping container ${OLDEST_CONTAINER} ..." 167 | 168 | if ! ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem "${SSH_USER}"@"${DEPLOYMENT_SERVER}" sudo docker stop "${OLDEST_CONTAINER}"; then 169 | echo "Failed to stop the ${OLDEST_CONTAINER} container" 170 | fi 171 | echo "Successfully stopped the ${OLDEST_CONTAINER} container." 172 | 173 | if ! ssh -o "StrictHostKeyChecking no" -i /tmp/sshkey.pem "${SSH_USER}"@"${DEPLOYMENT_SERVER}" sudo docker rm -f "${OLDEST_CONTAINER}"; then 174 | echo "Failed to remove the ${OLDEST_CONTAINER} container" 175 | fi 176 | echo "Successfully removed the ${OLDEST_CONTAINER} container." 177 | 178 | echo "Deploying the service: ${SERVICE_NAME}" 179 | deploy && sleep 30 180 | echo "Deployment completed." 181 | fi 182 | -------------------------------------------------------------------------------- /docker-compose.yml.template: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | ServiceName: 4 | restart: unless-stopped 5 | image: ImageName 6 | container_name: ContainerName 7 | pull_policy: always 8 | networks: 9 | - caddy 10 | labels: 11 | caddy_0: UrlName 12 | caddy_0.reverse_proxy: "{{upstreams PortNum}}" 13 | caddy_0.tls.ca: https://acme-staging-v02.api.letsencrypt.org/directory 14 | 15 | networks: 16 | caddy: 17 | external: true 18 | -------------------------------------------------------------------------------- /lib/FroalaEditor.jsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import FroalaEditorFunctionality from './FroalaEditorFunctionality.jsx'; 4 | import React from 'react'; 5 | 6 | export default class FroalaEditor extends FroalaEditorFunctionality { 7 | render () { 8 | return this.el = el}>{this.props.children}; 9 | } 10 | } -------------------------------------------------------------------------------- /lib/FroalaEditorA.jsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import FroalaEditorFunctionality from './FroalaEditorFunctionality.jsx'; 4 | import React from 'react'; 5 | 6 | export default class FroalaEditorA extends FroalaEditorFunctionality { 7 | 8 | render () { 9 | return ( 10 | this.el = el}>{this.props.children} 11 | ); 12 | } 13 | } -------------------------------------------------------------------------------- /lib/FroalaEditorButton.jsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import FroalaEditorFunctionality from './FroalaEditorFunctionality.jsx'; 4 | import React from 'react'; 5 | 6 | export default class FroalaEditorButton extends FroalaEditorFunctionality { 7 | 8 | render () { 9 | return ( 10 | 11 | ); 12 | } 13 | } -------------------------------------------------------------------------------- /lib/FroalaEditorFunctionality.jsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | 5 | let lastId = 0; 6 | let FroalaEditor = null; 7 | export default class FroalaEditorFunctionality extends React.Component { 8 | constructor(props) { 9 | super(props); 10 | 11 | // Tag on which the editor is initialized. 12 | this.defaultTag = 'div'; 13 | this.tag = props.tag || this.defaultTag; 14 | this.listeningEvents = []; 15 | 16 | // Jquery wrapped element. 17 | this.element = null; 18 | 19 | // Editor element. 20 | this.editor = null; 21 | 22 | // Editor options config 23 | this.config = { 24 | immediateReactModelUpdate: false, 25 | reactIgnoreAttrs: null 26 | }; 27 | 28 | this.editorInitialized = false; 29 | this.editorCreated = false; 30 | 31 | this.SPECIAL_TAGS = ['img', 'button', 'input', 'a']; 32 | this.INNER_HTML_ATTR = 'innerHTML'; 33 | this.hasSpecialTag = false; 34 | 35 | this.oldModel = null; 36 | } 37 | 38 | // After first time render. 39 | componentDidMount() { 40 | import(/*webpackIgnore: true*/ 'froala-editor').then((module) => { 41 | FroalaEditor = module.default; 42 | 43 | let tagName = this.el.tagName.toLowerCase(); 44 | if (this.SPECIAL_TAGS.indexOf(tagName) != -1) { 45 | this.tag = tagName; 46 | this.hasSpecialTag = true; 47 | } 48 | 49 | if (this.props.onManualControllerReady) { 50 | this.generateManualController(); 51 | } else { 52 | this.createEditor(); 53 | } 54 | }); 55 | } 56 | 57 | componentWillUnmount() { 58 | this.destroyEditor(); 59 | } 60 | 61 | componentDidUpdate() { 62 | if (JSON.stringify(this.oldModel) == JSON.stringify(this.props.model)) { 63 | return; 64 | } 65 | 66 | this.setContent(); 67 | } 68 | 69 | // Return cloned object 70 | clone(item) { 71 | const me = this; 72 | if (!item) { return item; } // null, undefined values check 73 | 74 | let types = [ Number, String, Boolean ], 75 | result; 76 | 77 | // normalizing primitives if someone did new String('aaa'), or new Number('444'); 78 | types.forEach(function(type) { 79 | if (item instanceof type) { 80 | result = type( item ); 81 | } 82 | }); 83 | 84 | if (typeof result == "undefined") { 85 | if (Object.prototype.toString.call( item ) === "[object Array]") { 86 | result = []; 87 | item.forEach(function(child, index, array) { 88 | result[index] = me.clone( child ); 89 | }); 90 | } else if (typeof item == "object") { 91 | // testing that this is DOM 92 | if (item.nodeType && typeof item.cloneNode == "function") { 93 | result = item.cloneNode( true ); 94 | } else if (!item.prototype) { // check that this is a literal 95 | if (item instanceof Date) { 96 | result = new Date(item); 97 | } else { 98 | // it is an object literal 99 | result = {}; 100 | for (var i in item) { 101 | result[i] = me.clone( item[i] ); 102 | } 103 | } 104 | } else { 105 | if (false && item.constructor) { 106 | result = new item.constructor(); 107 | } else { 108 | result = item; 109 | } 110 | } 111 | } else { 112 | result = item; 113 | } 114 | } 115 | return result; 116 | } 117 | 118 | createEditor() { 119 | if (this.editorInitialized || this.editorCreated) { 120 | return; 121 | } 122 | 123 | this.config = this.clone(this.props.config || this.config); 124 | this.config = {...this.config}; 125 | 126 | this.element = this.el; 127 | 128 | this.setContent(true); 129 | 130 | // Default initialized. 131 | this.registerEvent('initialized', this.config.events && this.config.events.initialized); 132 | 133 | // Check if events are set. 134 | if (!this.config.events) this.config.events = {}; 135 | this.config.events.initialized = () => this.initListeners(); 136 | 137 | this.editor = new FroalaEditor(this.element, this.config); 138 | this.editorCreated = true; 139 | } 140 | 141 | setContent(firstTime) { 142 | if (this.props.model || this.props.model == '') { 143 | this.oldModel = this.props.model; 144 | 145 | if (this.hasSpecialTag) { 146 | this.setSpecialTagContent(); 147 | } else { 148 | this.setNormalTagContent(firstTime); 149 | } 150 | } 151 | } 152 | 153 | setNormalTagContent(firstTime) { 154 | let self = this; 155 | 156 | function htmlSet() { 157 | self.editor.html && self.editor.html.set(self.props.model || ''); 158 | if (self.editorInitialized && self.editor.undo) { 159 | //This will reset the undo stack everytime the model changes externally. Can we fix this? 160 | //https://github.com/froala-labs/froala-editor-js-2/issues/4214 161 | if (!self.props.skipReset) { 162 | self.editor.undo.reset(); 163 | } 164 | self.editor.undo.saveStep(); 165 | } 166 | } 167 | 168 | if (firstTime) { 169 | if (this.config.initOnClick) { 170 | this.registerEvent('initializationDelayed', () => { 171 | htmlSet(); 172 | }); 173 | 174 | this.registerEvent('initialized', () => { 175 | this.editorInitialized = true; 176 | }); 177 | } else { 178 | this.registerEvent('initialized', () => { 179 | this.editorInitialized = true; 180 | htmlSet(); 181 | }); 182 | } 183 | } else { 184 | htmlSet(); 185 | } 186 | } 187 | 188 | setSpecialTagContent() { 189 | let tags = this.props.model; 190 | 191 | // add tags on element 192 | if (tags) { 193 | for (let attr in tags) { 194 | if (tags.hasOwnProperty(attr) && attr != this.INNER_HTML_ATTR) { 195 | this.element.setAttribute(attr, tags[attr]); 196 | } 197 | } 198 | 199 | if (tags.hasOwnProperty(this.INNER_HTML_ATTR)) { 200 | this.element.innerHTML = tags[this.INNER_HTML_ATTR]; 201 | } 202 | } 203 | } 204 | 205 | destroyEditor() { 206 | if (this.element) { 207 | this.editor.destroy && this.editor.destroy(); 208 | this.listeningEvents.length = 0; 209 | this.element = null; 210 | this.editorInitialized = false; 211 | this.editorCreated = false; 212 | this.config = { 213 | immediateReactModelUpdate: false, 214 | reactIgnoreAttrs: null 215 | }; 216 | let tagName = this.el.tagName.toLowerCase(); 217 | if (this.SPECIAL_TAGS.indexOf(tagName) == -1) { 218 | if(this.editor && this.editor.destrying && !this.props.onManualControllerReady && this.tag == 'textarea'){ 219 | this.editor.$box.remove() 220 | } 221 | } 222 | if(this.tag != 'textarea'){ 223 | this.editor.$wp = ''; 224 | } 225 | } 226 | } 227 | 228 | getEditor() { 229 | if (this.element) { 230 | return this.editor; 231 | } 232 | 233 | return null; 234 | } 235 | 236 | generateManualController() { 237 | let self = this; 238 | 239 | let controls = { 240 | initialize: () => self.createEditor.call(self), 241 | destroy: () => self.destroyEditor.call(self), 242 | getEditor: () => self.getEditor.call(self) 243 | }; 244 | 245 | this.props.onManualControllerReady(controls); 246 | } 247 | 248 | updateModel() { 249 | if (!this.props.onModelChange) { 250 | return; 251 | } 252 | 253 | let modelContent = ''; 254 | 255 | if (this.hasSpecialTag) { 256 | let attributeNodes = this.element.attributes; 257 | let attrs = {}; 258 | 259 | for (let i = 0; i < attributeNodes.length; i++) { 260 | let attrName = attributeNodes[i].name; 261 | if (this.config.reactIgnoreAttrs && this.config.reactIgnoreAttrs.indexOf(attrName) != -1) { 262 | continue; 263 | } 264 | attrs[attrName] = attributeNodes[i].value; 265 | } 266 | 267 | if (this.element.innerHTML) { 268 | attrs[this.INNER_HTML_ATTR] = this.element.innerHTML; 269 | } 270 | 271 | modelContent = attrs; 272 | } else { 273 | let returnedHtml = this.editor.html.get(); 274 | if (typeof returnedHtml === 'string') { 275 | modelContent = returnedHtml; 276 | } 277 | } 278 | 279 | this.oldModel = modelContent; 280 | this.props.onModelChange(modelContent); 281 | } 282 | 283 | initListeners() { 284 | let self = this; 285 | 286 | // bind contentChange and keyup event to froalaModel 287 | if(this.editor && this.editor.events){ 288 | this.editor.events.on('contentChanged', function () { 289 | self.updateModel(); 290 | }); 291 | } 292 | 293 | if (this.config.immediateReactModelUpdate) { 294 | this.editor.events.on('keyup', function () { 295 | self.updateModel(); 296 | }); 297 | } 298 | 299 | // Call init events. 300 | if (this._initEvents) { 301 | for (let i = 0; i < this._initEvents.length; i++) { 302 | this._initEvents[i].call(this.editor); 303 | } 304 | } 305 | } 306 | 307 | // register event on jquery editor element 308 | registerEvent(eventName, callback) { 309 | if (!eventName || !callback) { 310 | return; 311 | } 312 | 313 | if (eventName == 'initialized') { 314 | if (!this._initEvents) this._initEvents = []; 315 | this._initEvents.push(callback); 316 | } 317 | else { 318 | if (!this.config.events) { 319 | this.config.events = {}; 320 | } 321 | 322 | this.config.events[eventName] = callback; 323 | } 324 | } 325 | }; 326 | -------------------------------------------------------------------------------- /lib/FroalaEditorImg.jsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import FroalaEditorFunctionality from './FroalaEditorFunctionality.jsx'; 4 | import React from 'react'; 5 | 6 | export default class FroalaEditorImg extends FroalaEditorFunctionality { 7 | render () { 8 | return ( 9 | this.el = el}/> 10 | ); 11 | } 12 | } -------------------------------------------------------------------------------- /lib/FroalaEditorInput.jsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import FroalaEditorFunctionality from './FroalaEditorFunctionality.jsx'; 4 | import React from 'react'; 5 | 6 | export default class FroalaEditorInput extends FroalaEditorFunctionality { 7 | 8 | render () { 9 | return ( 10 | this.el = el}/> 11 | ); 12 | } 13 | } -------------------------------------------------------------------------------- /lib/FroalaEditorView.jsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | 5 | export default class FroalaEditorView extends React.Component { 6 | 7 | constructor(props) { 8 | super(props) 9 | 10 | this.defaultTag = 'div'; 11 | } 12 | 13 | getTrustedHtml () { 14 | return {__html: this.props.model}; 15 | } 16 | 17 | render () { 18 | this.tag = this.props.tag || this.defaultTag; 19 | return ( 20 | 21 | ); 22 | } 23 | } -------------------------------------------------------------------------------- /lib/copy_bundles.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | rm -rf ./bundle 3 | pwd 4 | cp -r ./dist/* ./ -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'react-froala-wysiwyg' 2 | { 3 | 4 | 5 | export interface MyComponentProps { 6 | tag?:string; 7 | config?: object; 8 | model?: string| object|null; 9 | onModelChange?: object; 10 | onManualControllerReady?: object; 11 | skipReset?: boolean|false; 12 | } 13 | 14 | 15 | export default class FroalaEditor extends React.Component{ 16 | destroy():void; 17 | getEditor(): any; 18 | initialize() : object; 19 | } 20 | 21 | } 22 | 23 | declare module 'react-froala-wysiwyg/FroalaEditorView' 24 | { 25 | export interface MyComponentProps { 26 | tag?:string; 27 | config?: object; 28 | model?: string| object|null; 29 | onModelChange?: object; 30 | onManualControllerReady?: object; 31 | skipReset?: boolean|false; 32 | } 33 | export default class FroalaEditorView extends React.Component 34 | { 35 | 36 | } 37 | } 38 | 39 | declare module 'react-froala-wysiwyg/FroalaEditorImg' 40 | { 41 | export interface MyComponentProps { 42 | tag?:string; 43 | config?: object; 44 | model?: string| object|null; 45 | onModelChange?: object; 46 | onManualControllerReady?: object; 47 | skipReset?: boolean|false; 48 | } 49 | export default class FroalaEditorImg extends React.Component 50 | { 51 | 52 | } 53 | } 54 | 55 | declare module 'react-froala-wysiwyg/FroalaEditorA' 56 | { 57 | export interface MyComponentProps { 58 | tag?:string; 59 | config?: object; 60 | model?: string| object|null; 61 | onModelChange?: object; 62 | onManualControllerReady?: object; 63 | skipReset?: boolean|false; 64 | } 65 | export default class FroalaEditorA extends React.Component 66 | { 67 | 68 | } 69 | } 70 | declare module 'react-froala-wysiwyg/FroalaEditorButton' 71 | { 72 | export interface MyComponentProps { 73 | tag?:string; 74 | config?: object; 75 | model?: string| object|null; 76 | onModelChange?: object; 77 | onManualControllerReady?: object; 78 | skipReset?: boolean|false; 79 | } 80 | export default class FroalaEditorButton extends React.Component 81 | { 82 | 83 | } 84 | } 85 | declare module 'react-froala-wysiwyg/FroalaEditorInput' 86 | { 87 | export interface MyComponentProps { 88 | tag?:string; 89 | config?: object; 90 | model?: string| object|null; 91 | onModelChange?: object; 92 | onManualControllerReady?: object; 93 | skipReset?: boolean|false; 94 | } 95 | export default class FroalaEditorInput extends React.Component 96 | { 97 | 98 | } 99 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-froala-wysiwyg", 3 | "version": "4.5.2", 4 | "description": "React component for Froala WYSIWYG HTML rich text editor.", 5 | "main": "index.js", 6 | "types": "./lib/index.d.ts", 7 | "author": "Froala Labs", 8 | "keywords": [ 9 | "react", 10 | "froala", 11 | "html", 12 | "text", 13 | "editor", 14 | "wysisyg", 15 | "rich editor", 16 | "rich text editor", 17 | "rte", 18 | "javascript" 19 | ], 20 | "peerDependencies": { 21 | "react": "~0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", 22 | "react-dom": "~0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" 23 | }, 24 | "dependencies": { 25 | "froala-editor": "4.5.2" 26 | }, 27 | "devDependencies": { 28 | "@babel/cli": "^7.25.9", 29 | "@babel/core": "^7.26.0", 30 | "@babel/plugin-proposal-decorators": "^7.25.9", 31 | "@babel/plugin-proposal-function-sent": "^7.25.9", 32 | "@babel/plugin-proposal-throw-expressions": "^7.25.9", 33 | "@babel/preset-env": "^7.26.0", 34 | "@babel/preset-react": "^7.25.9", 35 | "autoprefixer": "^10.4.20", 36 | "babel-loader": "^8.0.0", 37 | "copy-webpack-plugin": "^12.0.2", 38 | "core-js": "^3.39.0", 39 | "css-loader": "^7.1.2", 40 | "exports-loader": "^0.6.2", 41 | "file-loader": "^6.2.0", 42 | "highlight.js": "^11.10.0", 43 | "imports-loader": "^5.0.0", 44 | "jsdom": "^25.0.1", 45 | "mocha": "^10.8.2", 46 | "mock-require": "^1.3.0", 47 | "nightwatch": "^3.9.0", 48 | "postcss-loader": "^8.1.1", 49 | "raw-loader": "^0.5.1", 50 | "react": "^18.3.1", 51 | "react-dom": "^18.3.1", 52 | "react-highlight": "^0.15.0", 53 | "sinon": "^1.17.4", 54 | "style-loader": "^0.21.0", 55 | "url-loader": "^4.1.1", 56 | "webpack": "^5.96.1", 57 | "webpack-cli": "^5.1.4", 58 | "webpack-dev-server": "^5.1.0" 59 | }, 60 | "scripts": { 61 | "build": "webpack-cli --mode=development && webpack-cli --mode=production", 62 | "demo": "cd demo && webpack-cli --mode=development && \"../node_modules/.bin/webpack-dev-server\" --mode development --allowed-hosts all --port 4000 --host 0.0.0.0 --static dist/", 63 | "prepublishOnly": "npm run build && bash lib/copy_bundles.sh" 64 | }, 65 | "repository": { 66 | "type": "git", 67 | "url": "git+https://github.com/froala/react-froala-wysiwyg.git" 68 | }, 69 | "license": "MIT", 70 | "bugs": { 71 | "url": "https://github.com/froala/react-froala-wysiwyg/issues" 72 | }, 73 | "homepage": "https://github.com/froala/react-froala-wysiwyg#readme" 74 | } 75 | -------------------------------------------------------------------------------- /push-image-to-nexus.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export BRANCH_NAME=`echo "${TRAVIS_BRANCH}" | tr '[:upper:]' '[:lower:]'` 3 | case "${BRANCH_NAME}" in 4 | dev*) echo "Branch ${TRAVIS_BRANCH} is eligible for CI/CD" ;; 5 | ao-dev*)echo "Branch ${TRAVIS_BRANCH} is eligible for CI/CD" ;; 6 | qa*) echo "Branch ${TRAVIS_BRANCH} is eligible for CI/CD" ;; 7 | qe*) echo "Branch ${TRAVIS_BRANCH} is eligible for CI/CD" ;; 8 | rc*) echo "Branch ${TRAVIS_BRANCH} is eligible for CI/CD" ;; 9 | release-master*) echo "Branch ${TRAVIS_BRANCH} is eligible for CI/CD" ;; 10 | ft*) echo "Branch ${TRAVIS_BRANCH} is eligible for CI" ;; 11 | bf*) echo "Branch ${TRAVIS_BRANCH} is eligible for CI" ;; 12 | *) echo "Not a valid branch name for CI/CD" && exit -1;; 13 | esac 14 | 15 | echo $TRAVIS_BRANCH 16 | echo ${DEPLOYMENT_SERVER} 17 | export SHORT_COMMIT=`git rev-parse --short=7 ${TRAVIS_COMMIT}` 18 | echo "short commit $SHORT_COMMIT" 19 | sudo apt-get update 20 | sudo apt-get install -y jq 21 | IMAGE_NAME=`echo "${BUILD_REPO_NAME}_${TRAVIS_BRANCH}" | tr '[:upper:]' '[:lower:]'` 22 | PACKAGE_NAME=`jq '.name' version.json | tr -d '"'` 23 | PACKAGE_VERSION=`jq '.version' version.json | tr -d '"'` 24 | echo "Package name : ${PACKAGE_NAME}" 25 | jq --arg froalaeditor "file:${PACKAGE_NAME}-${PACKAGE_VERSION}.tgz" '.dependencies["froala-editor"] |= $froalaeditor' package.json > new.file && cat new.file > package.json && rm -f new.file 26 | echo "verify package" 27 | cat package.json 28 | docker build -t ${IMAGE_NAME}:${SHORT_COMMIT} --no-cache --build-arg PackageName=${PACKAGE_NAME} --build-arg PackageVersion=${PACKAGE_VERSION} --build-arg NexusUser=${NEXUS_USER} --build-arg NexusPassword=${NEXUS_USER_PWD} . 29 | sleep 3 30 | docker image ls 31 | echo "uploading to nexus ${PACKAGE_NAME}, if not a new PR" 32 | if [ ${TRAVIS_PULL_REQUEST} != "false" ]; then echo "Not publishing a pull request !!!" && exit 0; fi 33 | docker login -u ${NEXUS_USER} -p ${NEXUS_USER_PWD} ${NEXUS_CR_TOOLS_URL} 34 | docker tag ${IMAGE_NAME}:${SHORT_COMMIT} ${NEXUS_CR_TOOLS_URL}/froala-${IMAGE_NAME}:${PACKAGE_VERSION} 35 | docker push ${NEXUS_CR_TOOLS_URL}/froala-${IMAGE_NAME}:${PACKAGE_VERSION} 36 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": " Please update this file for any new branch with core libary name - package name and version uploaded to nexus", 3 | "name": "froala-editor-QA241122", 4 | "version": "4.0.16", 5 | "comment-2": "Please specify the maximum allowed deployments per environment", 6 | "dev": "1", 7 | "qa": "2", 8 | "qe": "2", 9 | "stg": "2" 10 | } 11 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | var froalaExternals = { 4 | 'froala-editor': { 5 | root: 'froala-editor', 6 | commonjs2: 'froala-editor/js', 7 | commonjs: 'froala-editor/js', 8 | amd: 'froala-editor/js' 9 | } 10 | }; 11 | 12 | var reactExternals = { 13 | react: { 14 | root: 'React', 15 | commonjs2: 'react', 16 | commonjs: 'react', 17 | amd: 'react' 18 | }, 19 | 'react-dom': { 20 | root: 'ReactDOM', 21 | commonjs2: 'react-dom', 22 | commonjs: 'react-dom', 23 | amd: 'react-dom' 24 | } 25 | }; 26 | 27 | var externals = [reactExternals,'froala-editor']; 28 | 29 | var config = { 30 | entry: { 31 | // Array syntax to workaround https://github.com/webpack/webpack/issues/300 32 | 'index': ['./lib/FroalaEditor.jsx'], 33 | 'FroalaEditorA': ['./lib/FroalaEditorA.jsx'], 34 | 'FroalaEditorButton': ['./lib/FroalaEditorButton.jsx'], 35 | 'FroalaEditorImg': ['./lib/FroalaEditorImg.jsx'], 36 | 'FroalaEditorInput': ['./lib/FroalaEditorInput.jsx'], 37 | 'FroalaEditorView': ['./lib/FroalaEditorView.jsx'] 38 | }, 39 | module: { 40 | 41 | rules: [ 42 | { 43 | test: /\.jsx$/, 44 | use: [ 45 | { 46 | loader: 'babel-loader', 47 | options: { 48 | cacheDirectory: true, 49 | presets: [ 50 | ['@babel/preset-env', { 51 | 'targets': { 52 | "ie": "11" 53 | }, 54 | "corejs": 3, 55 | "useBuiltIns": "entry" 56 | }], 57 | '@babel/preset-react'] 58 | } 59 | } 60 | ] 61 | } 62 | ] 63 | }, 64 | externals: externals, 65 | resolve: { 66 | modules: ['./node_modules'] 67 | }, 68 | output: { 69 | globalObject: 'this', 70 | publicPath: '', 71 | filename: '[name].js', 72 | libraryTarget: 'umd', 73 | library: '[name]' 74 | } 75 | }; 76 | 77 | module.exports = (env, argv) => { 78 | if (argv.mode === 'development') { 79 | config.output.filename = '[name].src.js'; 80 | } 81 | return config; 82 | } --------------------------------------------------------------------------------