 
                
                        読み込みが終了しない場合は、しばらく待つか、リロードを行なってください。
                    
                    
                        If loading does not finish, wait for a while or reload.
                    
                     
                            
                            エンジニア向けの情報を発信するブログです。
                            どなたでも発信できます。
                            お好きに利用していただれば幸いです。
                        
 
                
20240517追記
quill.jsのversion2.0がリリースされてたでおいぃぃぃぃ!!
https://quilljs.com/
変更履歴
20230320
reactのsizeのselectの箇所にwarningが発生していたので修正しました
20230827
reactでQuillのimportが抜けていたので追加しました

20230827追記
githubに公開しました。デモはこちら
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>quillテスト</title>
        <!-- 👇 Quill.jsと必要なcssファイルをcdnでインポート -->
        <script src="https://cdn.quilljs.com/1.3.7/quill.js"></script>
        <link href="https://cdn.quilljs.com/1.3.7/quill.snow.css" rel="stylesheet">
    </head>
    <body>
        <style>
            /* 👇 見やすいように中央寄せに整える */
            #app-container {
                margin: 0 auto;
                width: 50%;
                margin-top: 20vh;
            }
        </style>
        <div id="app-container">
            html
            <!-- 👇 quillを適用する要素の上にこんな感じで要素を組むとツールバーになる -->
            <!-- 詳細は https://quilljs.com/docs/modules/toolbar/ -->
            <div id="toolbar">
                <span class="ql-formats">
                    <select class="ql-size">
                        <option value="small"></option>
                        <option selected></option>
                        <option value="large"></option>
                        <option value="huge"></option>
                    </select>
                </span>
                <span class="ql-formats">
                    <button class="ql-code-block"></button>
                </span>
            </div>
            <!-- 👇 quillを適用する要素 -->
            <div id="app"></div>
        </div>
        <script>
            // 👇 quillを作成する      👇 quillの対象になる要素のid
            const quill = new Quill('#app', {
                // 👇 snow -> 入力モード bubble -> 参照(?)モード
                theme: 'snow',
                modules: {
                    toolbar: {
                                 // 👇 quillのツールバーはこいつですよid
                        container: '#toolbar',
                    }
                },
                // 👇 quillで適用する装飾のホワイトリスト(よくわからない方はありなしでツールバーのボタンをぽちぽちするのだ!)
                formats: [
                    'size', 'code-block',
                ]
            });
        </script>
    </body>
</html>

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>quillテスト</title>
        <script src="https://cdn.quilljs.com/1.3.7/quill.js"></script>
        <link href="https://cdn.quilljs.com/1.3.7/quill.snow.css" rel="stylesheet">
        <!-- 👇 font-awesomeインポート -->
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css">
    </head>
    <body>
        <style>
            #app-container {
                margin: 0 auto;
                width: 50%;
                margin-top: 20vh;
            }
        </style>
        <div id="app-container">
            html
            <div id="toolbar">
                <span class="ql-formats">
                    <select class="ql-size">
                        <option value="small"></option>
                        <option selected></option>
                        <option value="large"></option>
                        <option value="huge"></option>
                    </select>
                </span>
                <span class="ql-formats">
                    <button class="ql-code-block"></button>
                </span>
                <!-- 👇 button追加 -->
                <span class="ql-formats">
                                   <!-- 👇 class名の命名規則はすぐ下の"クリック時の挙動を定義する"のcode-block内のコメントアウトを読むのだ! -->
                    <button class="ql-addDivBlot">
                        <!-- 👇 font-awesomeじゃなくても良い(気になる方は調べてみるのだ!) -->
                        <i class="fa-sharp fa-solid fa-square-check"></i>
                    </button>
                </span>
            </div>
            <div id="app"></div>
        </div>
        <script>
            const quill = new Quill('#app', {
                theme: 'snow',
                modules: {
                    toolbar: {
                        container: '#toolbar',
                    }
                },
                formats: [
                    'size', 'code-block',
                ]
            });
        </script>
    </body>
</html>

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>quillテスト</title>
        <script src="https://cdn.quilljs.com/1.3.7/quill.js"></script>
        <link href="https://cdn.quilljs.com/1.3.7/quill.snow.css" rel="stylesheet">
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css">
    </head>
    <body>
        <style>
            #app-container {
                margin: 0 auto;
                width: 50%;
                margin-top: 20vh;
            }
        </style>
        <div id="app-container">
            html
            <div id="toolbar">
                <span class="ql-formats">
                    <select class="ql-size">
                        <option value="small"></option>
                        <option selected></option>
                        <option value="large"></option>
                        <option value="huge"></option>
                    </select>
                </span>
                <span class="ql-formats">
                    <button class="ql-code-block"></button>
                </span>
                <span class="ql-formats">
                    <button class="ql-addDivBlot">
                        <i class="fa-sharp fa-solid fa-square-check"></i>
                    </button>
                </span>
            </div>
            <div id="app"></div>
        </div>
        <script>
            const quill = new Quill('#app', {
                theme: 'snow',
                modules: {
                    toolbar: {
                        container: '#toolbar',
                        // 👇 handlersにql-"クラス名"に対応するfunction名を登録する
                        handlers: {
                            'addDivBlot': funcAddDivBlot,
                        }
                    }
                },
                formats: [
                    'size', 'code-block',
                ]
            });
            // 👇 handlersに登録したfunctionを定義する
            function funcAddDivBlot () {
                // 👇 一旦デバッグ用でこんな感じにしておく
                console.log('hello beginner engineer!');
            }
        </script>
    </body>
</html>

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>quillテスト</title>
        <script src="https://cdn.quilljs.com/1.3.7/quill.js"></script>
        <link href="https://cdn.quilljs.com/1.3.7/quill.snow.css" rel="stylesheet">
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css">
    </head>
    <body>
        <style>
            #app-container {
                margin: 0 auto;
                width: 50%;
                margin-top: 20vh;
            }
        </style>
        <div id="app-container">
            html
            <div id="toolbar">
                <span class="ql-formats">
                    <select class="ql-size">
                        <option value="small"></option>
                        <option selected></option>
                        <option value="large"></option>
                        <option value="huge"></option>
                    </select>
                </span>
                <span class="ql-formats">
                    <button class="ql-code-block"></button>
                </span>
                <span class="ql-formats">
                    <button class="ql-addDivBlot">
                        <i class="fa-sharp fa-solid fa-square-check"></i>
                    </button>
                </span>
            </div>
            <div id="app"></div>
        </div>
        <script>
            // 👇 BlockEmbedなるものをQuillから取得(これ以外にもimportできますが、どこを参照してimportして良いのか未だによくわかってないのでネットサーフィンを駆使して探し出すのだ!)
            const BlockEmbed = Quill.import('blots/block/embed');
            // 👇 insertしたい要素のclassを取得した親を継承して定義
            class DivBlot extends BlockEmbed {
                // 👇 おまじない
                static create (value) {
                    console.log(value);
                    // 👇 おまじないでnodeを取得
                    let node = super.create();
                    // 👇 nodeを返す(insertされる要素になります)
                    return node;
                }
            }
            // 👇 このblot(?)の名前は"div_blot"です(formatに登録する名前)
            DivBlot.blotName = 'div_blot';
            // 👇 このblot(?)のタグは"div"です("div" -> <div></div> "divdiv" -> <divdiv></divdiv>)
            DivBlot.tagName = 'div';
            // 👇 このblot(?)のclass名は"engineer_blog"です
            DivBlot.className = 'engineer_blog';
            // 👇 Quillにこのblotを登録します(もうよくわからん! > ヾ(*・∀・)/)
            Quill.register(DivBlot);
            const quill = new Quill('#app', {
                theme: 'snow',
                modules: {
                    toolbar: {
                        container: '#toolbar',
                        handlers: {
                            'addDivBlot': funcAddDivBlot,
                        }
                    }
                },
                formats: [
                                          // 👇 insertしたい要素名を登録
                    'size', 'code-block', 'div_blot',
                ]
            });
                        
            function funcAddDivBlot () {
                // 👇 selectionなるものをquillから取得  👇 おまじない
                const selection = quill.getSelection(true);
                // 👇 キャレットが当たっている位置を取得
                let cursor_index = selection.index;
                // 👇 embedを挿入します 👇 キャレットの位置に👇 div_blotの要素(format)を 👇 {}の引数を与えます
                quill.insertEmbed(cursor_index, 'div_blot', {text: "BeginnerEngineerBlog\nよろしくお願いします。"});
                // 👇 キャレットの位置を挿入した要素の一つ後ろに移動します
                quill.setSelection(cursor_index + 1);
            }
        </script>
    </body>
</html>


<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>quillテスト</title>
        <script src="https://cdn.quilljs.com/1.3.7/quill.js"></script>
        <link href="https://cdn.quilljs.com/1.3.7/quill.snow.css" rel="stylesheet">
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css">
    </head>
    <body>
        <style>
            #app-container {
                margin: 0 auto;
                width: 50%;
                margin-top: 20vh;
            }
        </style>
        <div id="app-container">
            html
            <div id="toolbar">
                <span class="ql-formats">
                    <select class="ql-size">
                        <option value="small"></option>
                        <option selected></option>
                        <option value="large"></option>
                        <option value="huge"></option>
                    </select>
                </span>
                <span class="ql-formats">
                    <button class="ql-code-block"></button>
                </span>
                <span class="ql-formats">
                    <button class="ql-addDivBlot">
                        <i class="fa-sharp fa-solid fa-square-check"></i>
                    </button>
                </span>
            </div>
            <div id="app"></div>
        </div>
        <script>
            const BlockEmbed = Quill.import('blots/block/embed');
            class DivBlot extends BlockEmbed {
                static create (value) {
                    let node = super.create();
                    // 👇 好きなようにカスタマイズ
                    node.style.display = 'flex';
                    let img = document.createElement('img');
                    img.src = 'https://begien.com/image/beginner_engineer_blog.png';
                    img.alt = 'begien.com';
                    img.style.width = '50%';
                    img.style.height = 'auto';
                    node.appendChild(img);
                    let span = document.createElement('span');
                    span.style.display = 'flex';
                    span.style.alignItems = 'center';
                    span.innerText = value.text;
                    node.appendChild(span);
                    return node;
                }
            }
            DivBlot.blotName = 'div_blot';
            DivBlot.tagName = 'div';
            DivBlot.className = 'engineer_blog';
            Quill.register(DivBlot);
            const quill = new Quill('#app', {
                theme: 'snow',
                modules: {
                    toolbar: {
                        container: '#toolbar',
                        handlers: {
                            'addDivBlot': funcAddDivBlot,
                        }
                    }
                },
                formats: [
                    'size', 'code-block', 'div_blot',
                ]
            });
            
            function funcAddDivBlot () {
                const selection = quill.getSelection(true);
                let cursor_index = selection.index;
                quill.insertEmbed(cursor_index, 'div_blot', {text: "BeginnerEngineerBlog\nよろしくお願いします。"});
                quill.setSelection(cursor_index + 1);
            }
        </script>
    </body>
</html>

import './App.css';
import {useState} from 'react';
// 👇 react-quillを使うためのモジュールをimport
import ReactQuill, {Quill} from 'react-quill';
import 'react-quill/dist/quill.snow.css';
// 👇 fontawesomeの導入はこちらがわかりやすいのだ! https://qiita.com/stin_dev/items/5755e14805e60718620c
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSquareCheck } from '@fortawesome/free-regular-svg-icons';
const formats = [
    'code-block',
    'size',
    'div_blot',
];
function CustomIcon () {
    return (
        <FontAwesomeIcon icon={faSquareCheck} />
    );
}
const modules = {
    toolbar: {
        container: "#toolbar",
        handlers: {
            "addDivBlot": funcAddDivBlot,
        }
    },
};
const BlockEmbed = Quill.import('blots/block/embed');
class DivBlot extends BlockEmbed {
    static create (value) {
        let node = super.create();
        node.style.display = 'flex';
        let img = document.createElement('img');
        img.src = 'https://begien.com/image/BeginnerEngineerBlogTopImage.png';
        img.alt = 'begien.com';
        img.style.width = '50%';
        img.style.height = 'auto';
        node.appendChild(img);
        let span = document.createElement('span');
        span.style.display = 'flex';
        span.style.alignItems = 'center';
        span.innerText = value.text;
        node.appendChild(span);
        return node;
    }
}
DivBlot.blotName = 'div_blot';
DivBlot.tagName = 'div';
DivBlot.className = 'beginner_engineer';
Quill.register(DivBlot, true);
function funcAddDivBlot () {
    const quill = this.quill;
    const selection = quill.getSelection(true);
    let cursor_index = selection.index;
    quill.insertEmbed(cursor_index, 'div_blot', {text: "BeginnerEngineerBlog\nよろしくお願いします。"});
    quill.setSelection(cursor_index + 1);
}
function App() {
    let app_style = {
        margin: '0 auto',
        width: '50%',
        marginTop: '20vh',
    };
    const [value, setValue] = useState('');
    return (
        <div className="App" style={app_style}>
            <div style={{textAlign: 'left'}}>react</div>
            <QuillToolbar
            />
            <ReactQuill
                theme="snow"
                value={value}
                onChange={setValue}
                modules={modules}
                formats={formats}
            />
        </div>
    );
}
function QuillToolbar (props) {
    return (
        <div id="toolbar" style={{display: 'flex'}}>
            <span className="ql-formats">
                <select className="ql-size" defaultValue="normal">
                    <option value="small"></option>
                    <option value="normal"></option>
                    <option value="large"></option>
                    <option value="huge"></option>
                </select>
            </span>
            <span className="ql-formats">
                <button className="ql-code-block"></button>
            </span>
            <span className="ql-formats">
                <button className="ql-addDivBlot">
                    <CustomIcon />
                </button>
            </span>
        </div>
    );
}
export default App;

20240517追記
冒頭に述べましたがversion2.0がリリースされました!🎉
今の所このサイトではversion2.0にアップデートしようと思ったのですがcode-blockが表示されずアップデートは諦めています(´・ω・`)
これからquill.jsを利用しようかと思っている方は2.0の利用をおすすめします!