Bootstrap 5 offcanvas & editor modal

Bootstrap 5 offcanvas & editor modal

BoinikBoinik Posts: 12Questions: 2Answers: 0

Hello. Can I somehow put editable fields in Bootstrap offcanvas?
https://getbootstrap.com/docs/5.2/components/offcanvas/#static-backdrop
Thanks for any reply

This question has accepted answers - jump to:

Answers

  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    Answer ✓

    You mean instead of a modal? That's really neat - I hadn't seen that UI in Bootstrap before.

    What you'd need to do for that is create a plug-in display controller which places the form into the offcanvas control.

    If you look int the editor.bootstrap5.js file, you'll see that we use a display controller to make it work with a native Bootstrap 5 modal. Basically the same thing would be done for offcanvas.

    Allan

  • BoinikBoinik Posts: 12Questions: 2Answers: 0

    Thanks Allan. Everything almost turned out, but for some reason the backdrop does not want to work.
    Perhaps I made a mistake somewhere. I don't have very good knowledge in Js

        (function () {
            let shown = false;
            let fullyShown = false;
    
            $.extend(true, $.fn.dataTable.Editor.classes, {
                "header": {
                    "wrapper": "DTE_Header offcanvas-header"
                },
                "body": {
                    "wrapper": "DTE_Body offcanvas-body"
                },
                "footer": {
                    "wrapper": "DTE_Footer modal-footer"
                },
                "form": {
                    "tag": "form-horizontal",
                    "button": "btn",
                    "buttonInternal": "btn btn-outline-secondary"
                },
                "field": {
                    "wrapper": "DTE_Field form-group row",
                    "label": "col-lg-8 col-form-label",
                    "input": "col-lg-12",
                    "error": "error is-invalid",
                    "msg-labelInfo": "form-text text-secondary small",
                    "msg-info": "form-text text-secondary small",
                    "msg-message": "form-text text-secondary small",
                    "msg-error": "form-text text-danger small",
                    "multiValue": "card multi-value",
                    "multiInfo": "small",
                    "multiRestore": "card multi-restore"
                }
            });
            const dom = {
                content: $(
                    '<div class="offcanvas offcanvas-end DTED_offcanvas" tabindex="-1" id="DTED_offcanvas" >' +
                    '<div class="DTED_offcanvas-content">' +
                    '</div>' +
                    '</div>'
                ),
                close: $('<button type="button" class="btn-close text-reset"></button>')
            };
            const offcanvas = new bootstrap.Offcanvas(dom.content[0], {
                backdrop:true
            });
    
            var Editor = $.fn.dataTable.Editor;
            Editor.display.bsOffcanvas = $.extend(true, {}, Editor.models.displayController, {
                init: function (dte) {
    
                    // Add `form-control` to required elements
                    dte.on('displayOrder.dtebs', function (e, display, action, form) {
                        $.each(dte.s.fields, function (key, field) {
                            $('input:not([type=checkbox]):not([type=radio]), select, textarea', field.node())
                                .addClass('form-control');
    
                            $('input[type=checkbox], input[type=radio]', field.node())
                                .addClass('form-check-input');
                        });
                    });
                    return DataTable.Editor.display.bsOffcanvas;
                },
    
                open: function (dte, append, callback) {
                   // console.log(offcanvas)
                    if (offcanvas._isShown){
                        $(dom.content)
                            .one('hidden.bs.offcanvas', function () {
                                $(this).detach();
                            });
                        offcanvas.hide();
                        shown = false;
                        fullyShown = false;
                        return;
                    }
                    $(append).find('div.DTE_Form_Buttons').addClass('d-grid gap-2 d-md-flex justify-content-md-center');
    
                    dom.close
                        .attr('title', dte.i18n.close)
                        .off('click.dte-bs5')
                        .on('click.dte-bs5', function () {
                            dte.close('icon');
                        })
                        .appendTo($('div.DTE_Header', append))
    
                    var content = dom.content.find('div.DTED_offcanvas-content');
                    content.children().detach();
                    content.append(append);
    
                    if (shown) {
                        if (callback) {
                            callback();
                        }
                        return;
                    }
    
                    shown = true;
                    fullyShown = false;
    
                    $(dom.content)
                        .one('shown.bs.offcanvas', function () {
                            // Can only give elements focus when shown
                            if (dte.s.setFocus) {
                                dte.s.setFocus.focus();
                            }
                            fullyShown = true;
                            if (callback) {
                                callback();
                            }
                        })
                        .one('hidden', function () {
                            shown = false;
                        })
                        .appendTo('body');
                    offcanvas.show();
                },
    
                close: function (dte, callback) {
                    if (!shown) {
                        if (callback) {
                            callback();
                        }
                        return;
                    }
    
                    // Check if actually displayed or not before hiding. BS4 doesn't like `hide`
                    // before it has been fully displayed
                    if (!fullyShown) {
                        $(dom.content)
                            .one('shown.bs.offcanvas', function () {
                                DataTable.Editor.display.bsOffcanvas.close(dte, callback);
                            });
    
                        return;
                    }
    
                    $(dom.content)
                        .one('hidden.bs.offcanvas', function () {
                            $(this).detach();
                        });
    
                    offcanvas.hide();
                    shown = false;
                    fullyShown = false;
    
                    if (callback) {
                        callback();
                    }
                },
                node: function (dte) {
                    return dom.content[0];
                }
            });
        })();
    
  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    Answer ✓

    For someone who claims to not have good knowledge of Javascript, I'd love to see a program in a language you do have good knowledge off!

    That's looking really good, and I think it might be a quirk of Bootstrap that the background isn't showing there. It is expecting the target DOM element to be in the document when initialised and isn't inserting the backdrop when it isn't.

    I've rejigged things a bit and come up with this which I think works nicely with BS 5.2.0:

    (function () {
        var Editor = $.fn.dataTable.Editor;
        
        Editor.display.bsOffcanvas = $.extend(
            true,
            {},
            Editor.models.displayController,
            {
                init: function (dte) {
                    // Add `form-control` to required elements
                    dte.on('displayOrder.dtebs', function (e, display, action, form) {
                        $.each(dte.s.fields, function (key, field) {
                            $(
                                'input:not([type=checkbox]):not([type=radio]), select, textarea',
                                field.node()
                            ).addClass('form-control');
    
                            $('input[type=checkbox], input[type=radio]', field.node()).addClass(
                                'form-check-input'
                            );
                        });
                    });
    
                    let content = $(
                        '<div class="offcanvas offcanvas-end DTED_offcanvas" tabindex="-1" id="DTED_offcanvas" ></div>'
                    ).appendTo('body');
    
                    dte._offcanvas = {
                        close: $('<button type="button" class="btn-close text-reset"></button>'),
                        content: content,
                        inst: new bootstrap.Offcanvas(content[0], {
                            backdrop: 'static'
                        }),
                        show: false,
                        fullyShown: false,
                        backdrop: null
                    };
    
                    return DataTable.Editor.display.bsOffcanvas;
                },
    
                open: function (dte, append, callback) {
                    let offcanvas = dte._offcanvas;
    
                    $(append)
                        .find('div.DTE_Form_Buttons')
                        .addClass('d-grid gap-2 d-md-flex justify-content-md-center');
    
                    offcanvas.close
                        .attr('title', dte.i18n.close)
                        .off('click.dte-bs5')
                        .on('click.dte-bs5', function () {
                            dte.close('icon');
                        })
                        .appendTo($('div.DTE_Header', append));
    
                    // There isn't a footer class for offcanvas, so we put the
                    // footer into the body
                    $('div.DTE_Body', append).append(
                        $('div.DTE_Footer', append)
                    );
    
                    var content = offcanvas.content;
                    content.children().detach();
                    content.append(append);
    
                    if (offcanvas.shown) {
                        if (callback) {
                            callback();
                        }
                        return;
                    }
    
                    offcanvas.shown = true;
                    offcanvas.fullyShown = false;
    
                    $(offcanvas.content)
                        .one('shown.bs.offcanvas', function () {
                            // Can only give elements focus when shown
                            if (dte.s.setFocus) {
                                dte.s.setFocus.focus();
                            }
    
                            offcanvas.fullyShown = true;
    
                            if (callback) {
                                callback();
                            }
                        })
                        .one('hidden', function () {
                            offcanvas.shown = false;
                        });
    
                    offcanvas.inst.show();
    
                    if (! offcanvas.backdrop) {
                        offcanvas.backdrop = dte._offcanvas.inst._backdrop._element;
    
                        $(offcanvas.backdrop).on('click', function () {
                            dte.background();
                        });
    
                        // Add BS offcanvas classes to the components
                        $('div.DTE_Header', append).addClass('offcanvas-header');
                        $('div.DTE_Body', append).addClass('offcanvas-body');
                    }
                },
    
                close: function (dte, callback) {
                    let offcanvas = dte._offcanvas;
    
                    if (! offcanvas.shown) {
                        if (callback) {
                            callback();
                        }
                        return;
                    }
    
                    // Check if actually displayed or not before hiding. BS4 doesn't like `hide`
                    // before it has been fully displayed
                    if (! offcanvas.fullyShown) {
                        $(offcanvas.content).one('shown.bs.offcanvas', function () {
                            DataTable.Editor.display.bsOffcanvas.close(dte, callback);
                        });
    
                        return;
                    }
    
                    offcanvas.inst.hide();
    
                    offcanvas.shown = false;
                    offcanvas.fullyShown = false;
    
                    if (callback) {
                        callback();
                    }
                },
                node: function (dte) {
                    return offcanvas.content[0];
                },
            }
        );
    })();
    

    Interestingly this has actually highlighted a flaw in my default display controller in that is is adding modal classes, even although we don't want this in this case. That's a bug I'll fix for 2.1.

    Would you mind if I publish this? If you are happy with that, what credit (i.e. link / name) would you like?

    Regards,
    Allan

  • BoinikBoinik Posts: 12Questions: 2Answers: 0

    Thanks Allan. Everything works perfectly. But before publishing, you need to fix 1 line of code

    new bootstrap.Offcanvas(content[0], {
                            backdrop: 'static'
    }),
    

    Replaced by :

    new bootstrap.Offcanvas(content[0], {
                            backdrop: true
    }),
    
    

    Or delete

    new bootstrap.Offcanvas(content[0], {}),
    

    And you can post. Use your name or mine on the forum
    PS result.

Sign In or Register to comment.