summernote-ext-tableプラグインに機能を追加する

前回導入/修正したsummernote-ext-tableにSummernoteに機能を追加してみる。

動作確認環境

  • jQuery(3.7.1)
  • Bootstrap(5.3.5)
  • Summernote(0.9.1)
    • summernote-cleaner(1.0.9)
    • summernote-ext-table(0.1a)※前回修正したバージョン

参考:前回までのソース

カラーパレットのカスタム色作成機能を有効にする

summernote-ext-tableで選択できるカラーパレットには何故かカスタム色の作成機能がコメントアウトされて使えなくなっているので使えるようにする。

コメントアウトされているものを解除するだけでは使えなかったので以下のように修正した。

js/summernote-ext-table.js



        self.colorPalette = function (className, tooltip, callbackFnc) {
            const isBackground = className === 'note-color-back'; 
            const palletTitle = isBackground ? lang.color.background : lang.jTable.borderColor;
            const colorResetText = isBackground ? lang.color.transparent : lang.color.resetToDefault;
            const customPickerId = `${isBackground ? 'back' : 'border'}ColorPicker-${options.id}`;
            const customPaletteId = `${isBackground ? 'back' : 'border'}ColorPalette-${options.id}`;
            return ui.buttonGroup({
                className: 'note-color ' + className,
                children : [
                    ui.button({
                        className: 'note-current-color-button',
                        contents : ui.icon(options.icons.font + ' note-recent-color'),
                        tooltip  : tooltip,
                        container: options.container,
                        click    : function (e) {
                            const $button = $(e.currentTarget);
                            const value = $button.attr('data-backColor');
                            callbackFnc(value);
                        },
                        callback : function ($button) {
                            const $recentColor = $button.find('.note-recent-color');
                            $recentColor.css('background-color', className == 'note-color-table-border' ? '#000000' : options.colorButton.backColor);
                            $button.attr('data-backColor', className == 'note-color-table-border' ? '#000000' : options.colorButton.backColor);
                            $recentColor.css('color', 'transparent');
                        },
                    }),
                    ui.button({
                        className: 'dropdown-toggle',
                        contents : ui.dropdownButtonContents('', options),
                        tooltip  : lang.color.more,
                        container: options.container,
                        data     : {
                            toggle: 'dropdown',
                        },
                    }),
                    ui.dropdown({
                        items   : ([
                            '<div class="note-palette">',
                            //'<div class="note-palette-title">' + lang.color.background + '</div>',
                            '<div class="note-palette-title">' + palletTitle + '</div>',
                            '<div>',
                            '<button type="button" class="note-color-reset btn btn-light" data-event="backColor" data-value="inherit">',
                            //lang.color.transparent,
                            colorResetText,
                            '</button>',
                            '</div>',
                            //'<div class="note-holder" data-event="backColor"/>',
                            '<div class="note-holder" data-event="backColor"><!-- colors --></div>',
                            '<div>',
                            // '<button type="button" class="note-color-select btn btn-light" data-event="openPalette" data-value="backColorPicker">',
                            // lang.color.cpSelect,
                            // '</button>',
                            // '<input type="color" id="backColorPicker" class="note-btn note-color-select-btn" value="' + options.colorButton.backColor + '" data-event="backColorPalette">',
                            // '</div>',
                            // '<div class="note-holder-custom" id="backColorPalette" data-event="backColor"/>',
                            // '</div>',
                            '<button type="button" class="note-color-select btn btn-light" data-event="openPalette" data-value="' + customPickerId + '">',
                            lang.color.cpSelect,
                            '</button>',
                            '<input type="color" id="' + customPickerId + '" class="note-btn note-color-select-btn" value="' + options.colorButton.backColor + '" data-event="' + customPaletteId + '">',
                            '</div>',
                            '<div class="note-holder-custom" id="' + customPaletteId + '" data-event="backColor"/>',
                            '</div>',
                        ].join('')),
                        callback: function ($dropdown) {


修正後の表示

テーブルの線のスタイルを変更できるようにする

summernote-ext-tableの導入により線の色を変更できるようになったが、線のスタイルを二重線や点線に変更できるような機能は無いので追加してみる。
見出しの挿入のようにスタイルを設定できる形式で実装する。

まず、summernote-ext-tableに線のスタイルを変更できる機能を以下のように作成する。
jWidthHeightReset関数の後ろに追加した。

js/summernote-ext-table.js



        var borderStyles = [
            {title: lang.jTable.default, className: 'table-bordered'},
        ];

        context.memo('button.jBorderStyles', function () {
            borderStyles = borderStyles.concat(options.jTable.borderStyles);

            return ui.buttonGroup([
                ui.button({
                    className: 'dropdown-toggle',
                    contents : ui.dropdownButtonContents(ui.icon(options.icons.magic), options),
                    tooltip  : lang.style.style,
                    container: options.container,
                    data     : {
                        toggle: 'dropdown',
                    },
                }),
                ui.dropdown({
                    className: 'jtable-border-style',
                    items: borderStyles,
                    template: function template(item) {
                        return '<p data-border-style="' + item.className + '">' + item.title + '</p>';
                    },
                    click: function (event) {
                        const $p = $(event.target);
                        const styleName = $p.data('border-style');
                        if (styleName == null) return;

                        self.beforeCommand();

                        const cell = tableBlock.currentTdEl;
                        const $cell = $(cell);
                        const $table = $cell.closest('table');

                        $.each(borderStyles, function(index, value) {
                            $table.removeClass(value.className);
                        });
                        $table.addClass(styleName);

                        resetTableBlock($cell);
            
                        self.afterCommand();
                    },
                }),
            ]).render();
        });


テーブルに渡す用のスタイルをいくつか作成する。

css/app.css



/* テーブルのスタイル */
/* 二重線 */
.table-double-bordered {
  border-width: 3px;
  border-style: double;
  border-collapse: collapse;
}
.table-double-bordered th, .table-double-bordered td {
  border-width: 1px;
  border-style: solid;
}
/* 強調二重線 */
.table-outline-bordered {
  border-width: 1px;
  border-style: solid;
  outline-width: 4px;
  outline-style: solid;
  outline-color:#dee2e6;
  outline-offset: 2px;
  border-collapse: collapse;
}
.table-outline-bordered th, .table-outline-bordered td {
  border-width: 1px;
  border-style: solid;
}
/* 点線 */
.table-dotted-bordered {
  border-width: 2px;
  border-style: dotted;
  border-collapse: collapse;
}
.table-dotted-bordered th, .table-dotted-bordered td {
  border-width: 2px;
  border-style: dotted;
}
/* 破線 */
.table-dashed-bordered {
  border-width: 2px;
  border-style: dashed;
  border-collapse: collapse;
}
.table-dashed-bordered th, .table-dashed-bordered td {
  border-width: 2px;
  border-style: dashed;
}
/* 下線のみ */
.table-bottom-bordered {
  border-bottom-width: 1px;
  border-bottom-style: solid;
  border-collapse: collapse;
}

下線のみや線なしのスタイルを追加する場合は、エディタ上のみ線を表示するようにすると扱いやすい。

例:下線のみ/線なしスタイルの場合はエディタ上のみ破線を表示する

css/summernote-bs5-editor-custom.css



/* エディタ上のテーブルスタイル */
/* 下線のみスタイルのエディタ上の表示 */
.note-editor .table-bottom-bordered th, .note-editor .table-bottom-bordered td {
  border-left: 1px dashed #dee2e6 !important;
  border-right: 1px dashed #dee2e6 !important;
}
/* 線なしスタイルのエディタ上の表示 */
.note-editor .table-borderless {
  border: 1px dashed #dee2e6 !important;
  border-collapse: collapse;
}
.note-editor .table-borderless th, .note-editor .table-borderless td {
  border: 1px dashed #dedee6 !important;
}

スタイル変更の設定をsummernoteの初期化処理に追加する。

summernote-init.js



      popover: {
        table: [
          //['merge', ['jMerge']],
          ['style', ['jBorderStyles', 'jBorderColor', 'jBackcolor', 'jAlign', 'jAddDeleteRowCol']],
          ['info', ['jTableInfo']],
          ['delete', ['jWidthHeightReset', 'deleteTable']],
        ]
      },
      styleTags: [
        {tag: 'h1', title: '主見出し', style: 'margin: 4px 0;', className: 'headline', value: 'h1'},
        {tag: 'h2', title: '見出し', style: 'margin: 4px 0;', className: 'headline', value: 'h2'},
        {tag: 'h3', title: '小見出し', style: 'margin: 4px 0;', className: 'headline', value: 'h3'},
      ],
      jTable: {
        //mergeMode: 'drag'
        borderStyles: [
          {title: '二重線', className: 'table-double-bordered'},
          {title: '強調二重線', className: 'table-outline-bordered'},
          {title: '点線', className: 'table-dotted-bordered'},
          {title: '破線', className: 'table-dashed-bordered'},
          {title: '下線のみ(編集時のみ縦破線あり)', className: 'table-bottom-bordered'},
          {title: '線無し(編集時のみ破線あり)', className: 'table-borderless'},
        ],
      },
      cleaner: {


機能追加結果

テーブルのスタイルをリセットする機能を追加する

先ほど追加した線の種類や線の色、セルの色をリセットする機能を追加してみる。
列幅や行の高さは既にテーブルサイズリセット機能があるため対象外とする。

リセット機能を先ほど追加した線のスタイル変更機能(jBorderStyles)の下に追加する。

js/summernote-ext-table.js



        context.memo('button.jTableStyleReset', function () {
            return ui.button({
                contents : ui.icon(options.icons.eraser),
                tooltip  : lang.table.table + ' ' + lang.jTable.styleReset,
                container: options.container,
                click    : context.createInvokeHandler('jTable.jTableStyleReset'),
            }).render();
        });

        self.jTableStyleReset = function () {
            const keepStyles = ['width', 'height'];
            const rng = modules.editor.getLastRange.call(modules.editor);
            if (rng.isCollapsed() && rng.isOnCell()) {
                self.beforeCommand();

                const cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
                const $table = $(cell).closest('table');

                $table.attr('class', options.tableClassName)
                const tableStyles = $table.attr('style');
                if (tableStyles) {
                    const newStyle = filterStyles(tableStyles, keepStyles);
                    if (newStyle !== '')
                        $table.attr('style', newStyle); 
                    else
                        $table.removeAttr('style');
                }

                $table.find('tr, td, th').each(function(index, cell) {
                    const $cell = $(cell);
                    const cellStyles = $cell.attr('style');
                    if (cellStyles) {
                        const newStyle = filterStyles(cellStyles, keepStyles);
                        if (newStyle !== '')
                            $cell.attr('style', newStyle); 
                        else
                            $cell.removeAttr('style');
                    }
                });

                self.afterCommand();
            }
        };

        function filterStyles(style, keepStyles) {
            return style.split(';').map(function(style) {
                const [property, value] = style.split(':').map(function(s) { return s.trim(); });
                return keepStyles.indexOf(property) !== -1 ? style: '';
            }).filter(function(style) {
                return style.trim() !== '';
            }).join(';');
        }


また、今回は機能はlang.jTable.styleResetという文言のプロパティを追加したので、各言語設定に文言を追加する。

js/summernote-ext-table.js



    $.extend(true, $.summernote.lang, {
        'en-US': {
            jTable: {
                borderColor    : 'border color',
                merge          : {
                    merge  : 'cell merge',
                    colspan: 'colspan',
                    rowspan: 'rowspan',
                    split  : 'cell split',
                },
                align          : {
                    top     : 'top',
                    middle  : 'middle',
                    bottom  : 'bottom',
                    baseline: 'baseline',
                },
                info           : {
                    info  : 'table info',
                    margin: 'margin'
                },
                apply          : 'apply',
                addDeleteRowCOl: 'Row/Col(Add/Del)',
                areaReset      : 'area Reset',
                styleReset     : 'style Reset',
                message        : '<b>Available after unmerge<br/>current or surrounding cells</br>',
            }
        },
        'ko-KR': {
            jTable: {
                borderColor    : '선색',
                merge          : {
                    merge  : '셀 합치기',
                    colspan: '가로',
                    rowspan: '세로',
                    split  : '셀 나누기',
                },
                align          : {
                    top     : '위쪽 정렬',
                    middle  : '가운데 정렬',
                    bottom  : '아래쪽 정렬',
                    baseline: '기본 정렬',
                },
                info           : {
                    info  : '테이블 정보',
                    margin: '여백'
                },
                apply          : '적용',
                addDeleteRowCOl: '행/열(추가/삭제)',
                areaReset      : '넓이/높이 초기화',
                styleReset     : '스타일 초기화',
                message        : '<b>현재 또는 주위 셀<br/>병합 해제 후 사용 가능</b>',
            }
        },
        'ja-JP': {
            jTable: {
                borderColor    : '線の色',
                merge          : {
                    merge  : 'セルの結合',
                    colspan: '列数',
                    rowspan: '行数',
                    split  : 'セルの分割',
                },
                align          : {
                    top     : '上揃え',
                    middle  : '中央揃え',
                    bottom  : '下揃え',
                    baseline: 'デフォルト',
                },
                info           : {
                    info  : 'テーブル情報',
                    margin: 'マージン設定'
                },
                apply          : '適用',
                addDeleteRowCOl: '行・列の追加/削除',
                areaReset      : 'サイズのリセット',
                styleReset     : 'スタイルのリセット',
                message        : '<div style="width:200px;text-align:left">現在のセルまたは周囲のセルの結合を解除した後に使用可能になります。</div>',
            }
        },
    });


スタイルリセットの設定をsummernoteの初期化処理に追加する。

js/summernote-init.js



      popover: {
        table: [
          //['merge', ['jMerge']],
          ['style', ['jBorderStyles', 'jBorderColor', 'jBackcolor', 'jAlign', 'jAddDeleteRowCol']],
          ['info', ['jTableInfo']],
          ['delete', ['jTableStyleReset', 'jWidthHeightReset', 'deleteTable']],
        ]
      },


機能追加結果

テーブルのスタイルリセット前

スタイルリセット後

テーブルの最大化機能を追加する

summernote-ext-tableでテーブルを作成した際は各列幅が100px固定で作成されるが、固定になってしまっていることによりテーブル全体の幅を100%にすることができない。
そのため、テーブルの幅を強制的に100%にする機能を追加する。

最大化機能を先ほど追加したfilterStyles関数の下に追加する。
実装方針はとしては、既にテーブルの幅が100%でなければcolgroup内の最後のcolの幅をリセットし、テーブルの幅を100%にする。

js/summernote-ext-table.js



        context.memo('button.jTableMaximize', function () {
            return ui.button({
                contents : '<i class="note-icon"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrows" viewBox="0 0 16 16"><path d="M1.146 8.354a.5.5 0 0 1 0-.708l2-2a.5.5 0 1 1 .708.708L2.707 7.5h10.586l-1.147-1.146a.5.5 0 0 1 .708-.708l2 2a.5.5 0 0 1 0 .708l-2 2a.5.5 0 0 1-.708-.708L13.293 8.5H2.707l1.147 1.146a.5.5 0 0 1-.708.708z"/></svg></i>',
                tooltip  : lang.table.table + ' ' + lang.jTable.maximize,
                container: options.container,
                click    : context.createInvokeHandler('jTable.jTableMaximize'),
            }).render();
        });

        self.jTableMaximize = function () {
            const rng = modules.editor.getLastRange.call(modules.editor);
            if (rng.isCollapsed() && rng.isOnCell()) {
                self.beforeCommand();

                const cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
                const $table = $(cell).closest('table');
                if ($table.css('width') !== '100%') {
                    const $lastCol = $table.find("colgroup col:last");
                    $lastCol.removeAttr('width');
                    $lastCol.css('width', '');
                    $table.removeAttr('width');
                    $table.css('width', '100%');
                }

                self.afterCommand();
            }
        };


また、今回もlang.jTable.maximizeという文言のプロパティを追加したので、各言語設定に文言を追加する。

js/summernote-ext-table.js



    $.extend(true, $.summernote.lang, {
        'en-US': {
            jTable: {
                borderColor    : 'border color',
                merge          : {
                    merge  : 'cell merge',
                    colspan: 'colspan',
                    rowspan: 'rowspan',
                    split  : 'cell split',
                },
                align          : {
                    top     : 'top',
                    middle  : 'middle',
                    bottom  : 'bottom',
                    baseline: 'baseline',
                },
                info           : {
                    info  : 'table info',
                    margin: 'margin'
                },
                apply          : 'apply',
                addDeleteRowCOl: 'Row/Col(Add/Del)',
                areaReset      : 'area Reset',
                styleReset     : 'style Reset',
                maximize       : 'maximize',
                message        : '<b>Available after unmerge<br/>current or surrounding cells</br>',
            }
        },
        'ko-KR': {
            jTable: {
                borderColor    : '선색',
                merge          : {
                    merge  : '셀 합치기',
                    colspan: '가로',
                    rowspan: '세로',
                    split  : '셀 나누기',
                },
                align          : {
                    top     : '위쪽 정렬',
                    middle  : '가운데 정렬',
                    bottom  : '아래쪽 정렬',
                    baseline: '기본 정렬',
                },
                info           : {
                    info  : '테이블 정보',
                    margin: '여백'
                },
                apply          : '적용',
                addDeleteRowCOl: '행/열(추가/삭제)',
                areaReset      : '넓이/높이 초기화',
                styleReset     : '스타일 초기화',
                maximize       : '최대화',
                message        : '<b>현재 또는 주위 셀<br/>병합 해제 후 사용 가능</b>',
            }
        },
        'ja-JP': {
            jTable: {
                borderColor    : '線の色',
                merge          : {
                    merge  : 'セルの結合',
                    colspan: '列数',
                    rowspan: '行数',
                    split  : 'セルの分割',
                },
                align          : {
                    top     : '上揃え',
                    middle  : '中央揃え',
                    bottom  : '下揃え',
                    baseline: 'デフォルト',
                },
                info           : {
                    info  : 'テーブル情報',
                    margin: 'マージン設定'
                },
                apply          : '適用',
                addDeleteRowCOl: '行・列の追加/削除',
                areaReset      : 'サイズのリセット',
                styleReset     : 'スタイルのリセット',
                maximize       : '最大化',
                message        : '<div style="width:200px;text-align:left">現在のセルまたは周囲のセルの結合を解除した後に使用可能になります。</div>',
            }
        },
    });


テーブル最大化の設定をsummernoteの初期化処理に追加する。

js/summernote-init.js



      popover: {
        table: [
          //['merge', ['jMerge']],
          ['style', ['jBorderStyles', 'jBorderColor', 'jBackcolor', 'jAlign', 'jAddDeleteRowCol', 'jTableMaximize']],
          ['info', ['jTableInfo']],
          ['delete', ['jTableStyleReset', 'jWidthHeightReset', 'deleteTable']],
        ]
      },


機能追加結果

テーブル最大化前

テーブル最大化後

全機能追加結果

コメント