├── .github └── workflows │ └── run-tests.yml ├── .styleci.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── composer.json ├── composer.lock ├── config └── config.php ├── mix-manifest.json ├── package-lock.json ├── package.json ├── public ├── editorjs.js └── editorjs.js.LICENSE.txt ├── resources ├── js │ └── editorjs.js └── views │ └── livewire │ └── editorjs.blade.php ├── src ├── Console │ ├── MakeEditorJsComponent.php │ └── stubs │ │ └── component.php.stub ├── Http │ └── Livewire │ │ └── EditorJS.php ├── LivewireEditorjs.php ├── LivewireEditorjsFacade.php └── LivewireEditorjsServiceProvider.php └── webpack.mix.js /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: run-tests 2 | 3 | on: 4 | push: 5 | schedule: 6 | - cron: '0 0 * * *' 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | php: [8.2, 8.1, 8.0, 7.4, 7.3] 14 | laravel: [10.*, 9.*, 8.*, 7.*] 15 | dependency-version: [prefer-stable] 16 | include: 17 | - laravel: 10.* 18 | testbench: 8.* 19 | - laravel: 9.* 20 | testbench: 7.* 21 | - laravel: 8.* 22 | testbench: 6.* 23 | - laravel: 7.* 24 | testbench: 5.* 25 | exclude: 26 | - php: 8.2 27 | laravel: 8.* 28 | - php: 8.2 29 | laravel: 7.* 30 | - php: 8.1 31 | laravel: 7.* 32 | - php: 8.0 33 | laravel: 10.* 34 | - php: 7.4 35 | laravel: 10.* 36 | - php: 7.4 37 | laravel: 9.* 38 | - php: 7.3 39 | laravel: 10.* 40 | - php: 7.3 41 | laravel: 9.* 42 | 43 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} - ${{ matrix.os }} 44 | 45 | steps: 46 | - name: Checkout code 47 | uses: actions/checkout@v2 48 | 49 | - name: Cache dependencies 50 | uses: actions/cache@v2 51 | with: 52 | path: ~/.composer/cache/files 53 | key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} 54 | 55 | - name: Setup PHP 56 | uses: shivammathur/setup-php@v2 57 | with: 58 | php-version: ${{ matrix.php }} 59 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql 60 | coverage: none 61 | 62 | - name: Install dependencies 63 | run: | 64 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update 65 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest 66 | 67 | - name: Execute tests 68 | run: vendor/bin/phpunit 69 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel 2 | 3 | disabled: 4 | - single_class_element_per_statement 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `livewire-editorjs` will be documented in this file 4 | 5 | ## 1.5.0 2023-04-07 6 | 7 | - Added support for Laravel 10 and PHP 8.2 8 | - Updated dependencies (EditorJS 2.22.3 -> 2.26.5) 9 | 10 | ## 1.4.0 2022-02-11 11 | 12 | Add support for Laravel 9 and PHP 8.1 13 | 14 | ## 1.3.0 2021-11-18 15 | 16 | Renamed `init` method of alpine component to `initEditor` for Alpine v3 compatibility. 17 | 18 | ## 1.2.0 2021-10-23 19 | 20 | Updated packaged EditorJS version to 2.22.3 21 | 22 | ## 1.1.0 2021-01-12 23 | 24 | - Automatically json_decode the passed in "value" prop to an assoc array when a string is passed in. 25 | 26 | ## 1.0.0 2021-01-08 27 | 28 | - Added option to disable the component of the package 29 | - Added config option to rename the component of the package 30 | - Added `placeholder` to the component 31 | - Added `style` attribute to the component 32 | - Added `logLevel` to the Editor.js config 33 | - Added make command to create Editor.js components 34 | - Added test suite 35 | 36 | ## 0.4.0 2021-01-04 37 | 38 | - Added `readOnly` mode to the Editor. 39 | - Lowered required Laravel version (7 & 8 supported) 40 | 41 | ## 0.3.0 - 2021-01-03 42 | 43 | Renamed method name for image upload. 44 | Like this it is more consistent when the `attaches` plugin of Editor.Js will be added 45 | 46 | ## 0.2.0 - 2021-01-03 47 | 48 | Use `editorId` in the view as container id for the Editor.js instance 49 | 50 | ## 0.1.0 - 2021-01-03 51 | 52 | Initial release of the package 53 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | Please read and understand the contribution guide before creating an issue or pull request. 6 | 7 | ## Etiquette 8 | 9 | This project is open source, and as such, the maintainers give their free time to build and maintain the source code 10 | held within. They make the code freely available in the hope that it will be of use to other developers. It would be 11 | extremely unfair for them to suffer abuse or anger for their hard work. 12 | 13 | Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the 14 | world that developers are civilized and selfless people. 15 | 16 | It's the duty of the maintainer to ensure that all submissions to the project are of sufficient 17 | quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. 18 | 19 | ## Viability 20 | 21 | When requesting or submitting new features, first consider whether it might be useful to others. Open 22 | source projects are used by many developers, who may have entirely different needs to your own. Think about 23 | whether or not your feature is likely to be used by other users of the project. 24 | 25 | ## Procedure 26 | 27 | Before filing an issue: 28 | 29 | - Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. 30 | - Check to make sure your feature suggestion isn't already present within the project. 31 | - Check the pull requests tab to ensure that the bug doesn't have a fix in progress. 32 | - Check the pull requests tab to ensure that the feature isn't already in progress. 33 | 34 | Before submitting a pull request: 35 | 36 | - Check the codebase to ensure that your feature doesn't already exist. 37 | - Check the pull requests to ensure that another person hasn't already submitted the feature or fix. 38 | 39 | ## Requirements 40 | 41 | If the project maintainer has any additional requirements, you will find them listed here. 42 | 43 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer). 44 | 45 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 46 | 47 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 48 | 49 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. 50 | 51 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 52 | 53 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 54 | 55 | **Happy coding**! 56 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Max Eckel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Easy integration of Editor.js in Laravel Livewire 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/maxeckel/livewire-editorjs.svg?style=for-the-badge)](https://packagist.org/packages/maxeckel/livewire-editorjs) 4 | [![Total Downloads](https://img.shields.io/packagist/dt/maxeckel/livewire-editorjs.svg?style=for-the-badge)](https://packagist.org/packages/maxeckel/livewire-editorjs) 5 | ![GitHub](https://img.shields.io/github/license/maxeckel/livewire-editorjs?style=for-the-badge) 6 | ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/maxeckel/livewire-editorjs/run-tests.yml?style=for-the-badge) 7 | 8 | This Package adds a Livewire component to your application, which will create an Editor.js instance for you. 9 | 10 | Out of the box it already supports image uploads using Livewire and the Image plugin of Editor.js. 11 | 12 | **Packaged version of Editor.js: 2.26.5** 13 | 14 | ## Requirements 15 | 16 | This package requires you to have the following tools installed: 17 | 18 | - [Laravel Livewire v2](https://laravel-livewire.com/docs/2.x/quickstart) 19 | - [Alpine.JS v2 or v3](https://alpinejs.dev/) 20 | 21 | Please refer to the linked guides on how to install these. 22 | 23 | ## Installation 24 | 25 | You can install the package via composer: 26 | 27 | ```bash 28 | composer require maxeckel/livewire-editorjs 29 | ``` 30 | 31 | After composer downloaded the package, you will need to publish it's config & assets: 32 | 33 | #### Config (optional) 34 | 35 | ```bash 36 | php artisan vendor:publish --tag=livewire-editorjs:config 37 | ``` 38 | 39 | #### Assets 40 | 41 | For the assets you have two options: 42 | 43 | 1. Already, for production, compiled assets including the above mentioned plugins: 44 | ```bash 45 | php artisan vendor:publish --tag=livewire-editorjs:assets:compiled --force 46 | ``` 47 | 48 | This will copy the compiled assets into `public/vendor/livewire-editorjs`. 49 | 50 | 2. Publish the raw assets to include them in your own build chain: 51 | ```bash 52 | php artisan vendor:publish --tag=livewire-editorjs:assets:raw 53 | ``` 54 | 55 | This will copy the raw assets into `resources/js/vendor/livewire-editorjs`. 56 | Now you have to include these assets into your own build chain. 57 | 58 | This method will be needed, if you want to further customize the editor, e.g. adding more plugins or 59 | configure the installed ones in a different way. 60 | 61 | #### IMPORTANT 62 | 63 | **For this to work, you will need to install all the plugins you want to use yourself!** 64 | **This also includes the ones already configured!** 65 | 66 | 67 | The last step is to include the scripts within your views. You can do this how ever you prefer. 68 | If you have chosen option 1. you can include the scripts with a little blade directive: 69 | 70 | `@livewireEditorjsScripts` 71 | 72 | **Advice** 73 | 74 | If you opted for option 1. of publishing the assets, you should make sure, that after a `composer update` 75 | the assets are published again to avoid them being outdated. In order to do so update your `composer.json` script 76 | configuration: 77 | 78 | ```json 79 | { 80 | "scripts": { 81 | "post-autoload-dump": [ 82 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", 83 | "@php artisan package:discover --ansi", 84 | "@php artisan vendor:publish --tag=livewire-editorjs:assets:compiled --force" 85 | ] 86 | } 87 | } 88 | ``` 89 | 90 | If you opted for option 2. I wouldn't suggest to automatically republish the raw assets, as this would overwrite any 91 | changes you made. Please check the changelog after you updated this package. 92 | 93 | ## Packaged Editor.js plugins 94 | 95 | - [Code](https://github.com/editor-js/code) 96 | - [Header](https://github.com/editor-js/header) 97 | - [Image](https://github.com/editor-js/image) Configured with Livewire Upload out of the box 98 | - [Inline Code](https://github.com/editor-js/inline-code) 99 | - [List](https://github.com/editor-js/list) 100 | - [Quote](https://github.com/editor-js/quote) 101 | - [Underline](https://github.com/editor-js/underline) 102 | 103 | ## Usage 104 | 105 | ``` php 106 | @livewire('editorjs', [ 107 | 'editorId' => "myEditor", 108 | 'value' => $value, 109 | 'uploadDisk' => 'public', 110 | 'downloadDisk' => 'public', 111 | 'class' => '...', 112 | 'style' => '...', 113 | 'readOnly' => true, 114 | 'placeholder' => 'Lorem ipsum dolor sit amet' 115 | ]) 116 | ``` 117 | 118 | ``` php 119 | 129 | ``` 130 | 131 | ### Properties / Parameters 132 | 133 | #### editorId 134 | 135 | The `editorId` parameter is used to generate a unique events from the Livewire component, 136 | in order for you to be able to listen for events of specific editors (in case more than 1 is used on the same page) 137 | 138 | **Important!** 139 | **Don't use the passed id anywhere else as `id` attribute on an HTML element, as the `editorId` is internally used 140 | as `id` on the wrapper `div` in which Editor.js gets initialized! 141 | If you use the `id` somewhere else, the instance will break!** 142 | 143 | #### value 144 | 145 | The `value` parameter sets the inital data for the editor instance. 146 | This would be your stored JSON data of Editor.js 147 | 148 | #### uploadDisk (optional) 149 | 150 | The `uploadDisk` parameter defines the disk, to which uploaded images should be stored. 151 | This parameter is optional. The default disk to store images in is defined within the packages config file: 152 | 153 | `config/livewire-editorjs.php` => `default_upload_img_disk` 154 | 155 | Default: `public` 156 | 157 | #### downloadDisk (optional) 158 | 159 | The `downloadDisk` parameter defines the disk, to which downloaded images should be stored. 160 | Images will be downloaded from the internet when a user pasts an image URL into the Editor (see [Editor.js image plugin](https://github.com/editor-js/image)) 161 | This parameter is optional. The default disk to store images in is defined within the packages config file: 162 | 163 | `config/livewire-editorjs.php` => `default_download_img_disk` 164 | 165 | Default: `public 166 | 167 | #### class (optional) 168 | 169 | As the name suggests you can pass in CSS classes as you would with any other component. 170 | For styling the Editor/Blocks, please refer to the [documentation](https://editorjs.io/styles) of Editor.js 171 | 172 | Default: "" 173 | 174 | #### style (optional) 175 | 176 | As the name suggest you can pass in inline styles as you would with any other component. 177 | 178 | Default: "" 179 | 180 | #### readOnly (optional) 181 | 182 | You can pass this parameter with an value of "true" to set the editor into read only mode. 183 | This might be useful, if you want to display articles the same way, as they have been created. 184 | 185 | Default: `false` 186 | 187 | #### placeholder (optional) 188 | 189 | Using the `placeholder` property, you can pass a placeholder to the Editor.js instance, which will 190 | be displayed in an empty editor. 191 | 192 | Default: '' (set through the corresponding config option `default_placeholder`) 193 | 194 | ### Events / Saving state 195 | 196 | The Editor is configured to save changes to the server using its built in `onChange` callback. 197 | In this callback the editor will sync its state with Livewire. 198 | When this happens, Livewire will `emitUp` a save event: 199 | 200 | `editorjs-save:editorId` 201 | 202 | The editorId is substituted, with the editorId parameter you passed to the component. 203 | With this, you can listen for the event within your Livewire Page/Component you use the editor in, 204 | in order to save changes to your models. 205 | 206 | ```php 207 | protected $listeners = ['editorjs-save:editorId' => 'saveEditorState']; 208 | 209 | public function saveEditorState($editorJsonData) 210 | { 211 | $this->model->data = $editorJsonData; 212 | } 213 | ``` 214 | 215 | ### Config 216 | 217 | In order to change the config, you'll first need to publish it: 218 | 219 | `php artisan vendor:publish --provider="Maxeckel\LivewireEditorjs\LivewireEditorjsServiceProvider" --tag="livewire-editorjs:config"` 220 | 221 | or 222 | 223 | `php artisan vendor:publish` and select `livewire-editorjs:config` by entering its number. 224 | 225 | #### Default config 226 | 227 | ```php 228 | true, 232 | 233 | 'component_name' => 'editorjs', 234 | 235 | // Sets the default placeholder to use when a new and empty Editor.js instance is created. 236 | 'default_placeholder' => '', 237 | 238 | /* 239 | * Available options: 240 | * 241 | * VERBOSE Show all messages (default) 242 | * INFO Show info and debug messages 243 | * WARN Show only warn messages 244 | * ERROR Show only error messages 245 | * 246 | * Taken from the offical docs of Editor.js: 247 | * https://editorjs.io/configuration#log-level 248 | */ 249 | 'editorjs_log_level' => 'ERROR', 250 | 251 | // Defines on which disk images, uploaded through the editor, should be stored. 252 | 'default_img_upload_disk' => 'public', 253 | 254 | // Defines on which disk images, downloaded by pasting an image url into the editor, should be stored. 255 | 'default_img_download_disk' => 'public', 256 | ]; 257 | 258 | ``` 259 | 260 | ##### enabled_component_registration (default: `true`) 261 | 262 | This option defines, whether the ServiceProvider should register the default livewire component while booting. 263 | Set this to `false` when you want to disable the internal component and use your own using the `make:editorjs` command. 264 | 265 | ##### component_name (default: `editorjs`) 266 | 267 | This option defines, under which name the internal component should be registered. 268 | By default this is set to `editorjs`, making the component accessible via "" or "@livewire('editorjs')". 269 | You can change this to whatever fits you best! 270 | 271 | ##### default_placeholder (default: `''`) 272 | 273 | This option sets a global default for the placeholder property of Editor.js. 274 | The placeholder will be displayed when an instance is created without any content. 275 | 276 | ##### editorjs_log_level (default: `'ERROR'`) 277 | 278 | This option sets the log level (console output) of Editor.js. 279 | The available options are: 280 | 281 | | Value | Definition | 282 | | ------------- | ---------------------------------- | 283 | | VERBOSE | Show all messages | 284 | | INFO | Show info and debug messages | 285 | | WARN | Show only warn messages | 286 | | ERROR | Show only error messages (default) | 287 | 288 | See [Editor.js docs](https://editorjs.io/configuration#log-level) for reference. 289 | 290 | ##### default_img_upload_disk (default: `public`) 291 | 292 | This option defines, to which disk uploaded images should be stored. 293 | Even though the disk can be changed on a per instance level, this option lets you set a global default. 294 | This is always used, when you don't provide a disk name to the component instance through its props. 295 | 296 | ##### default_img_download_disk (default: `public`) 297 | 298 | This option defines, to which disk downloaded images from the web should be stored. 299 | Even though the disk can be changed on a per instance level, this option lets you set a global default. 300 | This is always used, when you don't provide a disk name to the component instance through its props. 301 | 302 | ### Commands 303 | 304 | This package adds an `make:editorjs` command to your project. 305 | With this command it's possible for you to create your own EditorJs livewire component. 306 | This makes it possible for you to change and/or customize the component. 307 | 308 | If you add your own component this way, you should disable the packages internal component registration 309 | by setting `enabled_component_registration` in the `livewire-editorjs.php` config file to `false`. 310 | 311 | **Important!** 312 | **By using this method to create your own component, any updates to the packages component won't affect you!** 313 | **Which means any enhancements made won't be accessible to you.** 314 | 315 | ### Extending 316 | 317 | If you want to customize the component or extend its functionality, the best way is to extend the component 318 | provided by this package. That way, you will receive updates and still can customize the internals. 319 | 320 | ```php 321 | true, 5 | 6 | 'component_name' => 'editorjs', 7 | 8 | // Sets the default placeholder to use when a new and empty Editor.js instance is created. 9 | 'default_placeholder' => '', 10 | 11 | /* 12 | * Available options: 13 | * 14 | * VERBOSE Show all messages (default) 15 | * INFO Show info and debug messages 16 | * WARN Show only warn messages 17 | * ERROR Show only error messages 18 | * 19 | * Taken from the offical docs of Editor.js: 20 | * https://editorjs.io/configuration#log-level 21 | */ 22 | 'editorjs_log_level' => 'ERROR', 23 | 24 | // Defines on which disk images, uploaded through the editor, should be stored. 25 | 'default_img_upload_disk' => 'public', 26 | 27 | // Defines on which disk images, downloaded by pasting an image url into the editor, should be stored. 28 | 'default_img_download_disk' => 'public', 29 | ]; 30 | -------------------------------------------------------------------------------- /mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/public/editorjs.js": "/public/editorjs.js" 3 | } 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "development": "mix", 4 | "watch": "mix watch", 5 | "watch-poll": "mix watch -- --watch-options-poll=1000", 6 | "hot": "mix watch --hot", 7 | "production": "mix --production" 8 | }, 9 | "devDependencies": { 10 | "@editorjs/code": "^2.7.0", 11 | "@editorjs/editorjs": "^2.22.3", 12 | "@editorjs/header": "^2.6.1", 13 | "@editorjs/image": "^2.6.1", 14 | "@editorjs/inline-code": "^1.3.1", 15 | "@editorjs/list": "^1.6.2", 16 | "@editorjs/quote": "^2.4.0", 17 | "@editorjs/underline": "^1.0.0", 18 | "cross-env": "^7.0.2", 19 | "laravel-mix": "^6.0", 20 | "vue-template-compiler": "^2.6.12" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /public/editorjs.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * Image tool 3 | * 4 | * @version 2.8.1 5 | * 6 | * @package https://github.com/editor-js/image 7 | * @licence MIT 8 | * @author CodeX 9 | */ 10 | 11 | /*! For license information please see editor.js.LICENSE.txt */ 12 | 13 | /** 14 | * CodeTool for Editor.js 15 | * 16 | * @author CodeX (team@ifmo.su) 17 | * @copyright CodeX 2018 18 | * @license MIT 19 | * @version 2.0.0 20 | */ 21 | 22 | /** 23 | * Header block for the Editor.js. 24 | * 25 | * @author CodeX (team@ifmo.su) 26 | * @copyright CodeX 2018 27 | * @license MIT 28 | * @version 2.0.0 29 | */ 30 | 31 | /** 32 | * Image Tool for the Editor.js 33 | * 34 | * @author CodeX 35 | * @license MIT 36 | * @see {@link https://github.com/editor-js/image} 37 | * 38 | * To developers. 39 | * To simplify Tool structure, we split it to 4 parts: 40 | * 1) index.js — main Tool's interface, public API and methods for working with data 41 | * 2) uploader.js — module that has methods for sending files via AJAX: from device, by URL or File pasting 42 | * 3) ui.js — module for UI manipulations: render, showing preloader, etc 43 | * 4) tunes.js — working with Block Tunes: render buttons, handle clicks 44 | * 45 | * For debug purposes there is a testing server 46 | * that can save uploaded files and return a Response {@link UploadResponseFormat} 47 | * 48 | * $ node dev/server.js 49 | * 50 | * It will expose 8008 port, so you can pass http://localhost:8008 with the Tools config: 51 | * 52 | * image: { 53 | * class: ImageTool, 54 | * config: { 55 | * endpoints: { 56 | * byFile: 'http://localhost:8008/uploadFile', 57 | * byUrl: 'http://localhost:8008/fetchUrl', 58 | * } 59 | * }, 60 | * }, 61 | */ 62 | -------------------------------------------------------------------------------- /resources/js/editorjs.js: -------------------------------------------------------------------------------- 1 | import EditorJS from '@editorjs/editorjs'; 2 | import ImageTool from '@editorjs/image'; 3 | import List from '@editorjs/list'; 4 | import Header from '@editorjs/header'; 5 | import Underline from '@editorjs/underline'; 6 | import Code from '@editorjs/code'; 7 | import InlineCode from '@editorjs/inline-code'; 8 | import Quote from '@editorjs/quote'; 9 | 10 | window.editorInstance = function(dataProperty, editorId, readOnly, placeholder, logLevel) { 11 | return { 12 | instance: null, 13 | data: null, 14 | 15 | initEditor() { 16 | this.data = this.$wire.get(dataProperty); 17 | 18 | this.instance = new EditorJS({ 19 | holder: editorId, 20 | 21 | readOnly, 22 | 23 | placeholder, 24 | 25 | logLevel, 26 | 27 | tools: { 28 | image: { 29 | class: ImageTool, 30 | 31 | config: { 32 | uploader: { 33 | uploadByFile: (file) => { 34 | return new Promise((resolve) => { 35 | this.$wire.upload( 36 | 'uploads', 37 | file, 38 | (uploadedFilename) => { 39 | const eventName = `fileupload:${uploadedFilename.substr(0, 20)}`; 40 | 41 | const storeListener = (event) => { 42 | resolve({ 43 | success: 1, 44 | file: { 45 | url: event.detail.url 46 | } 47 | }); 48 | 49 | window.removeEventListener(eventName, storeListener); 50 | }; 51 | 52 | window.addEventListener(eventName, storeListener); 53 | 54 | this.$wire.call('completedImageUpload', uploadedFilename, eventName); 55 | } 56 | ); 57 | }); 58 | }, 59 | 60 | uploadByUrl: (url) => { 61 | return this.$wire.loadImageFromUrl(url).then(result => { 62 | return { 63 | success: 1, 64 | file: { 65 | url: result 66 | } 67 | } 68 | }); 69 | } 70 | } 71 | } 72 | }, 73 | list: List, 74 | header: Header, 75 | underline: Underline, 76 | code: Code, 77 | 'inline-code': InlineCode, 78 | quote: Quote 79 | }, 80 | 81 | data: this.data, 82 | 83 | onChange: () => { 84 | this.instance.save().then((outputData) => { 85 | this.$wire.set(dataProperty, outputData); 86 | 87 | this.$wire.call('save'); 88 | }).catch((error) => { 89 | console.log('Saving failed: ', error) 90 | }); 91 | } 92 | }); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /resources/views/livewire/editorjs.blade.php: -------------------------------------------------------------------------------- 1 |
8 |
9 |
10 | -------------------------------------------------------------------------------- /src/Console/MakeEditorJsComponent.php: -------------------------------------------------------------------------------- 1 | editorId = $editorId; 62 | $this->data = $value; 63 | $this->class = $class; 64 | $this->style = $style; 65 | $this->readOnly = $readOnly; 66 | $this->placeholder = $placeholder; 67 | $this->uploadDisk = $uploadDisk; 68 | $this->downloadDisk = $downloadDisk; 69 | 70 | $this->logLevel = config('livewire-editorjs.editorjs_log_level'); 71 | } 72 | 73 | public function completedImageUpload(string $uploadedFileName, string $eventName) 74 | { 75 | /** @var TemporaryUploadedFile $tmpFile */ 76 | $tmpFile = collect($this->uploads) 77 | ->filter(function (TemporaryUploadedFile $item) use ($uploadedFileName) { 78 | return $item->getFilename() === $uploadedFileName; 79 | }) 80 | ->first(); 81 | 82 | $storedFileName = $tmpFile->store('/', $this->uploadDisk); 83 | 84 | $this->dispatchBrowserEvent($eventName, [ 85 | 'url' => Storage::disk($this->uploadDisk)->url($storedFileName), 86 | ]); 87 | } 88 | 89 | public function loadImageFromUrl(string $url) 90 | { 91 | $name = basename($url); 92 | $content = file_get_contents($url); 93 | 94 | Storage::disk($this->downloadDisk)->put($name, $content); 95 | 96 | return Storage::disk($this->downloadDisk)->url($name); 97 | } 98 | 99 | public function save() 100 | { 101 | $this->emitUp("editorjs-save:{$this->editorId}", $this->data); 102 | } 103 | 104 | public function render() 105 | { 106 | return view('livewire-editorjs::livewire.editorjs'); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Http/Livewire/EditorJS.php: -------------------------------------------------------------------------------- 1 | editorId = $editorId; 63 | $this->data = $value; 64 | $this->class = $class; 65 | $this->style = $style; 66 | $this->readOnly = $readOnly; 67 | $this->placeholder = $placeholder; 68 | $this->uploadDisk = $uploadDisk; 69 | $this->downloadDisk = $downloadDisk; 70 | 71 | $this->logLevel = config('livewire-editorjs.editorjs_log_level'); 72 | } 73 | 74 | public function completedImageUpload(string $uploadedFileName, string $eventName, $fileName = null) 75 | { 76 | /** @var TemporaryUploadedFile $tmpFile */ 77 | $tmpFile = collect($this->uploads) 78 | ->filter(function (TemporaryUploadedFile $item) use ($uploadedFileName) { 79 | return $item->getFilename() === $uploadedFileName; 80 | }) 81 | ->first(); 82 | 83 | // When no file name is passed, we use the hashName of the tmp file 84 | $storedFileName = $tmpFile->storeAs( 85 | '/'.$this->imagesPath, 86 | $fileName ?? $tmpFile->hashName(), 87 | $this->uploadDisk 88 | ); 89 | 90 | $this->dispatchBrowserEvent($eventName, [ 91 | 'url' => Storage::disk($this->uploadDisk)->url($storedFileName), 92 | ]); 93 | } 94 | 95 | public function loadImageFromUrl(string $url) 96 | { 97 | $name = basename($url); 98 | $content = file_get_contents($url); 99 | 100 | Storage::disk($this->downloadDisk)->put($name, $content); 101 | 102 | return Storage::disk($this->downloadDisk)->url($name); 103 | } 104 | 105 | public function save() 106 | { 107 | $this->emitUp("editorjs-save:{$this->editorId}", $this->data); 108 | } 109 | 110 | public function render() 111 | { 112 | return view('livewire-editorjs::livewire.editorjs'); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/LivewireEditorjs.php: -------------------------------------------------------------------------------- 1 | loadViewsFrom(__DIR__.'/../resources/views', 'livewire-editorjs'); 20 | 21 | $this->registerPublishables(); 22 | $this->registerDirectives(); 23 | $this->registerCommands(); 24 | } 25 | 26 | /** 27 | * Register the application services. 28 | */ 29 | public function register() 30 | { 31 | // Automatically apply the package configuration 32 | $this->mergeConfigFrom(__DIR__.'/../config/config.php', 'livewire-editorjs'); 33 | 34 | // Add Livewire component 35 | $this->app->afterResolving(BladeCompiler::class, function () { 36 | $enabledComponentRegistration = config('livewire-editorjs.enabled_component_registration', true); 37 | 38 | if (class_exists(Livewire::class) && $enabledComponentRegistration) { 39 | $componentName = config('livewire-editorjs.component_name', 'editorjs'); 40 | 41 | Livewire::component($componentName, EditorJS::class); 42 | } 43 | }); 44 | 45 | // Register the main class to use with the facade 46 | $this->app->singleton('livewire-editorjs', function () { 47 | return new LivewireEditorjs; 48 | }); 49 | } 50 | 51 | private function registerPublishables() 52 | { 53 | if (! $this->app->runningInConsole()) { 54 | return; 55 | } 56 | 57 | $this->publishes([ 58 | __DIR__.'/../config/config.php' => config_path('livewire-editorjs.php'), 59 | ], 'livewire-editorjs:config'); 60 | 61 | // Publishing the views. 62 | $this->publishes([ 63 | __DIR__.'/../resources/views' => resource_path('views/vendor/livewire-editorjs'), 64 | ], 'livewire-editorjs:views'); 65 | 66 | // Publishing JS assets. 67 | $this->publishes([ 68 | __DIR__.'/../resources/js' => resource_path('js/vendor/livewire-editorjs'), 69 | ], 'livewire-editorjs:assets:raw'); 70 | 71 | // Publishing compiled JS assets. 72 | $this->publishes([ 73 | __DIR__.'/../public' => public_path('vendor/livewire-editorjs'), 74 | ], 'livewire-editorjs:assets:compiled'); 75 | } 76 | 77 | private function registerDirectives() 78 | { 79 | Blade::directive('livewireEditorjsScripts', function () { 80 | $scriptsUrl = asset('/vendor/livewire-editorjs/editorjs.js'); 81 | 82 | return << 84 | EOF; 85 | }); 86 | } 87 | 88 | private function registerCommands() 89 | { 90 | if (! $this->app->runningInConsole()) { 91 | return; 92 | } 93 | 94 | $this->commands([ 95 | MakeEditorJsComponent::class, 96 | ]); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | let mix = require('laravel-mix') 2 | 3 | mix.js('resources/js/editorjs.js', 'public/') 4 | --------------------------------------------------------------------------------