From cf06cdc10f94d6c6c2d22795af6e90e730e228d1 Mon Sep 17 00:00:00 2001
From: Campii <dominik.chrzanowski183@gmail.com>
Date: Thu, 14 Oct 2021 10:14:11 +0200
Subject: [PATCH] feat: create basic files structure for new brick
 TextAndImageMulti. added first functionality and settings.
 quiqqer/package-bricks#130

---
 .../Slider/PromosliderSettingsOnlyContent.js  |   2 +-
 bin/Controls/TextAndImageMultipleSettings.css |   8 +
 bin/Controls/TextAndImageMultipleSettings.js  | 761 ++++++++++++++++++
 .../TextAndImageMultipleSettingsEntry.html    |  50 ++
 bricks.xml                                    | 107 +++
 locale.xml                                    | 121 +++
 .../Bricks/Controls/TextAndImageMultiple.css  |   3 +
 .../Bricks/Controls/TextAndImageMultiple.php  | 106 +++
 8 files changed, 1157 insertions(+), 1 deletion(-)
 create mode 100644 bin/Controls/TextAndImageMultipleSettings.css
 create mode 100644 bin/Controls/TextAndImageMultipleSettings.js
 create mode 100644 bin/Controls/TextAndImageMultipleSettingsEntry.html
 create mode 100644 src/QUI/Bricks/Controls/TextAndImageMultiple.css
 create mode 100644 src/QUI/Bricks/Controls/TextAndImageMultiple.php

diff --git a/bin/Controls/Slider/PromosliderSettingsOnlyContent.js b/bin/Controls/Slider/PromosliderSettingsOnlyContent.js
index 43f7a81..9ad6ec1 100644
--- a/bin/Controls/Slider/PromosliderSettingsOnlyContent.js
+++ b/bin/Controls/Slider/PromosliderSettingsOnlyContent.js
@@ -17,7 +17,7 @@ define('package/quiqqer/bricks/bin/Controls/Slider/PromosliderSettingsOnlyConten
     'utils/Controls',
 
     'text!package/quiqqer/bricks/bin/Controls/Slider/PromosliderSettingsOnlyContentEntry.html',
-    'css!package/quiqqer/bricks/bin/Controls/Slider/PromoSliderSettings.css'
+    'css!package/quiqqer/bricks/bin/Controls/Slider/TextAndImageMultipleSettings.css'
 
 ], function (QUI, QUIControl, QUIConfirm, QUIButton, QUISwitch, QUILocale,
              Mustache, Grid, ControlsUtils, templateEntry) {
diff --git a/bin/Controls/TextAndImageMultipleSettings.css b/bin/Controls/TextAndImageMultipleSettings.css
new file mode 100644
index 0000000..2c5f1fc
--- /dev/null
+++ b/bin/Controls/TextAndImageMultipleSettings.css
@@ -0,0 +1,8 @@
+.quiqqer-bricks-promoslider-settings-entry-form table {
+    border: none;
+}
+
+.quiqqer-bricks-promoslider-settings-entry-form td {
+    padding-left: 0;
+    padding-right: 0;
+}
diff --git a/bin/Controls/TextAndImageMultipleSettings.js b/bin/Controls/TextAndImageMultipleSettings.js
new file mode 100644
index 0000000..3309d33
--- /dev/null
+++ b/bin/Controls/TextAndImageMultipleSettings.js
@@ -0,0 +1,761 @@
+/**
+ * @module package/quiqqer/bricks/bin/Controls/Slider/TextAndImageMultipleSettings
+ * @author Dominik Chrzanowski
+ *
+ */
+define('package/quiqqer/bricks/bin/Controls/TextAndImageMultipleSettings', [
+
+    'qui/QUI',
+    'qui/controls/Control',
+    'qui/controls/windows/Confirm',
+    'qui/controls/buttons/Button',
+    'qui/controls/buttons/Switch',
+    'Locale',
+    'Mustache',
+    'controls/grid/Grid',
+    'utils/Controls',
+
+    'text!package/quiqqer/bricks/bin/Controls/TextAndImageMultipleSettingsEntry.html',
+    'css!package/quiqqer/bricks/bin/Controls/TextAndImageMultipleSettings.css'
+
+], function (QUI, QUIControl, QUIConfirm, QUIButton, QUISwitch, QUILocale, Mustache, Grid, ControlsUtils, templateEntry) {
+    "use strict";
+
+    var lg = 'quiqqer/bricks';
+
+    return new Class({
+
+        Extends: QUIControl,
+        Type   : 'package/quiqqer/bricks/bin/Controls/Slider/PromosliderSettings',
+
+        Binds: [
+            '$onImport',
+            '$openAddDialog',
+            '$openDeleteDialog',
+            '$openEditDialog',
+            '$toggleSlideStatus',
+            'update'
+        ],
+
+        initialize: function (options) {
+            this.parent(options);
+
+            this.$Input = null;
+            this.$Grid  = null;
+
+            this.$data = [];
+
+            this.addEvents({
+                onImport: this.$onImport
+            });
+        },
+
+        /**
+         * event: on import
+         */
+        $onImport: function () {
+            this.$Input = this.getElm();
+
+            this.$Elm = new Element('div', {
+                'class': 'quiqqer-bricks-textAndImageMultiple-settings',
+                styles : {
+                    clear   : 'both',
+                    'float' : 'left',
+                    height  : 400,
+                    overflow: 'hidden',
+                    position: 'relative',
+                    margin  : '10px 0 0 0',
+                    width   : '100%'
+                }
+            }).wraps(this.$Input);
+
+            // grid and sizes
+            var size = this.$Elm.getSize();
+
+            var Desktop = new Element('div', {
+                styles: {
+                    width: size.x
+                }
+            }).inject(this.$Elm);
+
+            this.$Grid = new Grid(Desktop, {
+                height     : 400,
+                width      : size.x,
+                buttons    : [{
+                    name    : 'up',
+                    icon    : 'fa fa-angle-up',
+                    disabled: true,
+                    events  : {
+                        onClick: function () {
+                            this.$Grid.moveup();
+                            this.$refreshSorting();
+                        }.bind(this)
+                    }
+                }, {
+                    name    : 'down',
+                    icon    : 'fa fa-angle-down',
+                    disabled: true,
+                    events  : {
+                        onClick: function () {
+                            this.$Grid.movedown();
+                            this.$refreshSorting();
+                        }.bind(this)
+                    }
+                }, {
+                    type: 'separator'
+                }, {
+                    name     : 'add',
+                    textimage: 'fa fa-plus',
+                    text     : QUILocale.get('quiqqer/system', 'add'),
+                    events   : {
+                        onClick: this.$openAddDialog
+                    }
+                }, {
+                    type: 'separator'
+                }, {
+                    name     : 'edit',
+                    textimage: 'fa fa-edit',
+                    text     : QUILocale.get('quiqqer/system', 'edit'),
+                    disabled : true,
+                    events   : {
+                        onClick: this.$openEditDialog
+                    }
+                }, {
+                    name     : 'delete',
+                    textimage: 'fa fa-trash',
+                    text     : QUILocale.get('quiqqer/system', 'delete'),
+                    disabled : true,
+                    events   : {
+                        onClick: this.$openDeleteDialog
+                    }
+                }],
+                columnModel: [{
+                    header   : QUILocale.get(lg, 'quiqqer.bricks.textAndImageMultiple.create.isDisabled.short'),
+                    dataIndex: 'isDisabledDisplay',
+                    dataType : 'QUI',
+                    width    : 70
+                }, {
+                    dataIndex: 'isDisabled',
+                    hidden   : true
+                }, {
+                    header   : QUILocale.get('quiqqer/system', 'image'),
+                    dataIndex: 'imagePreview',
+                    dataType : 'node',
+                    width    : 60
+                }, {
+                    header   : QUILocale.get('quiqqer/system', 'title'),
+                    dataIndex: 'title',
+                    dataType : 'string',
+                    width    : 300
+                },
+                //     {
+                //     header   : QUILocale.get(lg, 'quiqqer.bricks.promoslider.create.align'),
+                //     dataIndex: 'type',
+                //     dataType : 'string',
+                //     width    : 200
+                // },
+                //     {
+                //     header   : QUILocale.get(lg, 'quiqqer.bricks.promoslider.create.url'),
+                //     dataIndex: 'url',
+                //     dataType : 'string',
+                //     width    : 300
+                // },
+                //     {
+                //     header   : QUILocale.get(lg, 'quiqqer.bricks.promoslider.create.newTab.short'),
+                //     dataIndex: 'newTabDisplay',
+                //     dataType : 'node',
+                //     width    : 60
+                // },
+                    {
+                    dataIndex: 'newTab',
+                    hidden   : true
+                }, {
+                    dataIndex: 'image',
+                    dataType : 'string',
+                    hidden   : true
+                }]
+            });
+
+            this.$Grid.addEvents({
+                onClick: function () {
+                    var buttons = this.$Grid.getButtons(),
+
+                        Edit    = buttons.filter(function (Btn) {
+                            return Btn.getAttribute('name') === 'edit';
+                        })[0],
+
+                        Up      = buttons.filter(function (Btn) {
+                            return Btn.getAttribute('name') === 'up';
+                        })[0],
+
+                        Down    = buttons.filter(function (Btn) {
+                            return Btn.getAttribute('name') === 'down';
+                        })[0],
+
+                        Delete  = buttons.filter(function (Btn) {
+                            return Btn.getAttribute('name') === 'delete';
+                        })[0];
+
+                    Up.enable();
+                    Down.enable();
+                    Edit.enable();
+                    Delete.enable();
+                }.bind(this),
+
+                onDblClick: this.$openEditDialog
+            });
+
+            this.$Grid.getElm().setStyles({
+                position: 'absolute'
+            });
+
+
+            try {
+                this.$data = JSON.decode(this.$Input.value);
+
+                if (typeOf(this.$data) !== 'array') {
+                    this.$data = [];
+                }
+
+                this.refresh();
+            } catch (e) {
+            }
+        },
+
+        /**
+         * Toggles the slide's status between enabled and disabled
+         *
+         * @param {Object} [Caller] - the object calling this event
+         */
+        $toggleSlideStatus: function (Caller) {
+            if (!Caller) {
+                return;
+            }
+
+            // get cell number
+            var row = Caller.getElm().getParent('li').get('data-row');
+
+            this.$data[row].isDisabled = Caller.getStatus();
+            this.update();
+        },
+
+        /**
+         * Resize the control
+         *
+         * @return {Promise}
+         */
+        resize: function () {
+            var size = this.getElm().getSize();
+
+            return this.$Grid.setWidth(size.x).then(function () {
+                this.$Grid.resize();
+            }.bind(this));
+        },
+
+        /**
+         * refresh the display
+         */
+        refresh: function () {
+            var i, len, entry, insert;
+            var data = [];
+
+            for (i = 0, len = this.$data.length; i < len; i++) {
+                entry  = this.$data[i];
+                insert = {};
+
+                entry.isDisabled = parseInt(entry.isDisabled);
+
+                insert.isDisabledDisplay = new QUISwitch({
+                    status: entry.isDisabled,
+                    name  : i,
+                    uid   : i,
+                    events: {
+                        onChange: this.$toggleSlideStatus
+                    }
+                });
+
+                if ("image" in entry && entry.image !== '' && entry.image !== 'test;') {
+                    insert.image = entry.image;
+
+                    insert.imagePreview = new Element('img', {
+                        src: URL_DIR + insert.image + '&maxwidth=50&maxheight=50'
+                    });
+                }
+
+                if ("title" in entry) {
+                    insert.title = entry.title;
+                }
+
+                if ("text" in entry) {
+                    insert.text = entry.text;
+                }
+
+                // if ("type" in entry) {
+                //     insert.type = entry.type;
+                // }
+                //
+                // if ("url" in entry) {
+                //     insert.url = entry.url;
+                // }
+                //
+                // if ("newTab" in entry) {
+                //     insert.newTabDisplay = new Element('span', {
+                //         'class'       : entry.newTab === "1" ? 'fa fa-check' : 'fa fa-times',
+                //         'data-enabled': entry.newTab
+                //     });
+                // }
+
+                data.push(insert);
+            }
+
+            this.$Grid.setData({
+                data: data
+            });
+
+            var buttons = this.$Grid.getButtons(),
+
+                Edit    = buttons.filter(function (Btn) {
+                    return Btn.getAttribute('name') === 'edit';
+                })[0],
+
+                Up      = buttons.filter(function (Btn) {
+                    return Btn.getAttribute('name') === 'up';
+                })[0],
+
+                Down    = buttons.filter(function (Btn) {
+                    return Btn.getAttribute('name') === 'down';
+                })[0],
+
+                Delete  = buttons.filter(function (Btn) {
+                    return Btn.getAttribute('name') === 'delete';
+                })[0];
+
+            Up.disable();
+            Down.disable();
+            Edit.disable();
+            Delete.disable();
+        },
+
+        /**
+         * Update the field
+         */
+        update: function () {
+            this.$Input.value = JSON.encode(this.$data);
+
+            console.log(this.$Input.value);
+            console.log(this.$Input);
+        },
+
+        /**
+         * Add an entry
+         *
+         * @param {Object} params
+         */
+        add: function (params) {
+            var entry = {
+                image     : '',
+                title     : '',
+                text      : '',
+                // type      : '',
+                // url       : '',
+                isDisabled: 0,
+                // newTab    : 1
+            };
+
+            if ("isDisabled" in params) {
+                entry.isDisabled = parseInt(params.isDisabled);
+            }
+
+            if ("image" in params && params.image !== '') {
+                entry.image = params.image;
+            }
+
+            if ("title" in params) {
+                entry.title = params.title;
+            }
+
+            if ("text" in params) {
+                entry.text = params.text;
+            }
+
+            // if ("type" in params) {
+            //     entry.type = params.type;
+            // }
+            //
+            // if ("url" in params) {
+            //     entry.url = params.url;
+            // }
+            //
+            // if ("newTab" in params) {
+            //     entry.newTab = parseInt(params.newTab);
+            // }
+
+            this.$data.push(entry);
+            this.refresh();
+            this.update();
+        },
+
+        /**
+         * Edit an entry
+         *
+         * @param {number} index
+         * @param {object} params
+         */
+        edit: function (index, params) {
+            if (typeof index === 'undefined') {
+                return;
+            }
+
+            var entry = {
+                image     : '',
+                title     : '',
+                text      : '',
+                // type      : '',
+                // url       : '',
+                // newTab    : '',
+                isDisabled: 0
+            };
+
+            if ("isDisabled" in params) {
+                entry.isDisabled = parseInt(params.isDisabled);
+            }
+
+            if ("image" in params) {
+                entry.image = params.image;
+            }
+
+            if ("title" in params) {
+                entry.title = params.title;
+            }
+
+            if ("text" in params) {
+                entry.text = params.text;
+            }
+
+            // if ("type" in params) {
+            //     entry.type = params.type;
+            // }
+            //
+            // if ("url" in params) {
+            //     entry.url = params.url;
+            // }
+            //
+            // if ("newTab" in params) {
+            //     entry.newTab = parseInt(params.newTab);
+            // }
+
+            this.$data[index] = entry;
+
+            this.refresh();
+            this.update();
+        },
+
+        /**
+         * Delete one entry or multiple entries
+         *
+         * @param {number|array} index
+         */
+        del: function (index) {
+            var newList = [];
+
+            if (typeOf(index) !== 'array') {
+                index = [index];
+            }
+
+            for (var i = 0, len = this.$data.length; i < len; i++) {
+                if (!index.contains(i)) {
+                    newList.push(this.$data[i]);
+                }
+            }
+
+            this.$data = newList;
+        },
+
+        /**
+         * Set the used project
+         *
+         * @param {string|object} Project
+         */
+        setProject: function (Project) {
+            this.setAttribute('project', Project);
+
+            var controls = QUI.Controls.getControlsInElement(this.getElm());
+
+            controls.each(function (Control) {
+                if (Control === this) {
+                    return;
+                }
+
+                if ("setProject" in Control) {
+                    Control.setProject(Project);
+                }
+            }.bind(this));
+        },
+
+        /**
+         * Refresh the data sorting in dependence of the grid
+         */
+        $refreshSorting: function () {
+            var gridData = this.$Grid.getData(),
+                data     = [];
+
+            for (var i = 0, len = gridData.length; i < len; i++) {
+                data.push({
+                    isDisabled: parseInt(gridData[i].isDisabled),
+                    image     : gridData[i].image,
+                    title     : gridData[i].title,
+                    text      : gridData[i].text,
+                    // type      : gridData[i].type,
+                    // url       : gridData[i].url
+                });
+            }
+
+            this.$data = data;
+            this.update();
+        },
+
+        /**
+         * Dialogs
+         */
+
+        /**
+         * opens the delete dialog
+         *
+         * @return {Promise}
+         */
+        $openDeleteDialog: function () {
+            new QUIConfirm({
+                icon       : 'fa fa-icon',
+                title      : QUILocale.get(lg, 'quiqqer.bricks.entires.delete.title'),
+                text       : QUILocale.get(lg, 'quiqqer.bricks.entires.delete.text'),
+                information: QUILocale.get(lg, 'quiqqer.bricks.entires.delete.information'),
+                texticon   : false,
+                maxWidth   : 600,
+                maxHeight  : 400,
+                ok_button  : {
+                    text     : QUILocale.get('quiqqer/system', 'delete'),
+                    textimage: 'fa fa-trash'
+                },
+                events     : {
+                    onSubmit: function () {
+                        var selected = this.$Grid.getSelectedIndices();
+
+                        this.$Grid.deleteRows(selected);
+                        this.del(selected);
+                        this.update();
+                    }.bind(this)
+                }
+            }).open();
+        },
+
+        /**
+         * Open edit dialog
+         *
+         * @retrun {Promise}
+         */
+        $openEditDialog: function () {
+            var self  = this,
+                data  = this.$Grid.getSelectedData(),
+                index = this.$Grid.getSelectedIndices();
+
+            if (!data.length) {
+                return Promise.resolve();
+            }
+
+            data  = data[0];
+            index = index[0];
+
+            return this.$createDialog().then(function (Dialog) {
+                Dialog.addEvent('onSubmit', function () {
+                    Dialog.Loader.show();
+
+                    var Content = Dialog.getContent();
+                    var Form    = Content.getElement('form');
+
+                    var Image       = Form.elements.image;
+                    var Title       = Form.elements.title;
+                    var Description = Form.elements.description;
+                    // var Type        = Form.elements.type;
+                    // var Url         = Form.elements.url;
+
+                    self.edit(index, {
+                        image     : Image.value,
+                        title     : Title.value,
+                        text      : Description.value,
+                        // type      : Type.value,
+                        // url       : Url.value,
+                        // newTab    : Dialog.NewTabSwitch.getStatus(),
+                        isDisabled: Dialog.IsDisabledSwitch.getStatus()
+                    });
+
+                    Dialog.close();
+                });
+
+
+                Dialog.addEvent('onOpenAfterCreate', function () {
+                    var Content = Dialog.getContent();
+                    var Form    = Content.getElement('form');
+
+                    var Image       = Form.elements.image;
+                    var Title       = Form.elements.title;
+                    var Description = Form.elements.description;
+                    var Type        = Form.elements.type;
+                    var Url         = Form.elements.url;
+
+                    if (data.isDisabled) {
+                        Dialog.IsDisabledSwitch.on();
+                    } else {
+                        Dialog.IsDisabledSwitch.off();
+                    }
+
+                    Image.value       = data.image;
+                    Title.value       = data.title;
+                    Description.value = data.text;
+                    // Type.value        = data.type;
+                    // Url.value         = data.url;
+
+                    if (data.newTab && data.newTab.getAttribute('data-enabled') === "1") {
+                        Dialog.NewTabSwitch.on();
+                    } else {
+                        Dialog.NewTabSwitch.off();
+                    }
+
+                    Image.fireEvent('change');
+                    Description.fireEvent('change');
+                });
+
+                Dialog.setAttribute('title', QUILocale.get(lg, 'quiqqer.bricks.entires.editdialog.title'));
+                Dialog.open();
+            });
+        },
+
+        /**
+         * opens the add dialog
+         *
+         * @return {Promise}
+         */
+        $openAddDialog: function () {
+            var self = this;
+
+            return this.$createDialog().then(function (Dialog) {
+                Dialog.addEvent('onSubmit', function () {
+                    Dialog.Loader.show();
+
+                    var Content = Dialog.getContent();
+                    var Form    = Content.getElement('form');
+
+                    var Image       = Form.elements.image;
+                    var Title       = Form.elements.title;
+                    var Description = Form.elements.description;
+                    // var Type        = Form.elements.type;
+                    // var Url         = Form.elements.url;
+
+                    self.add({
+                        image     : Image.value,
+                        title     : Title.value,
+                        text      : Description.value,
+                        // type      : Type.value,
+                        // url       : Url.value,
+                        // newTab    : Dialog.NewTabSwitch.getStatus(),
+                        isDisabled: Dialog.IsDisabledSwitch.getStatus()
+                    });
+
+                    Dialog.close();
+                });
+
+                Dialog.open();
+            });
+        },
+
+        /**
+         * Create a edit / add entry dialog
+         *
+         * @return {Promise}
+         */
+        $createDialog: function () {
+            var self = this;
+
+            return new Promise(function (resolve) {
+                var Dialog = new QUIConfirm({
+                    title           : QUILocale.get(lg, 'quiqqer.bricks.entires.adddialog.title'),
+                    icon            : 'fa fa-edit',
+                    maxWidth        : 800,
+                    maxHeight       : 600,
+                    autoclose       : false,
+                    IsDisabledSwitch: false,
+                    NewTabSwitch    : false,
+                    events          : {
+                        onOpen: function (Win) {
+                            Win.Loader.show();
+                            Win.getContent().set('html', '');
+
+                            var Container = new Element('div', {
+                                html   : Mustache.render(templateEntry, {
+                                    fieldIsDisabled : QUILocale.get(lg, 'quiqqer.bricks.promoslider.create.isDisabled'),
+                                    fieldImage      : QUILocale.get(lg, 'quiqqer.bricks.promoslider.create.image'),
+                                    // fieldUrl        : QUILocale.get(lg, 'quiqqer.bricks.promoslider.create.url'),
+                                    // fieldNewTab     : QUILocale.get(lg, 'quiqqer.bricks.promoslider.create.newTab'),
+                                    fieldTitle      : QUILocale.get(lg, 'quiqqer.bricks.promoslider.create.title'),
+                                    fieldDescription: QUILocale.get(lg, 'quiqqer.bricks.promoslider.create.text'),
+                                    // fieldType       : QUILocale.get(lg, 'quiqqer.bricks.promoslider.create.align'),
+                                    // fieldTypeLeft   : QUILocale.get(lg, 'quiqqer.bricks.promoslider.create.align.left'),
+                                    // fieldTypeRight  : QUILocale.get(lg, 'quiqqer.bricks.promoslider.create.align.right')
+                                }),
+                                'class': 'quiqqer-bricks-promoslider-settings-entry'
+                            }).inject(Win.getContent());
+
+                            var Text = Container.getElement('.field-description');
+
+                            Text.getParent().setStyles({
+                                height: 100
+                            });
+
+
+                            Win.IsDisabledSwitch = new QUISwitch({
+                                name  : 'isDisabled',
+                                status: false
+                            }).inject(Container.getElement('#isDisabledWrapper'))
+
+                            Win.NewTabSwitch = new QUISwitch({
+                                name: 'newTab'
+                            }).inject(Container.getElement('#newTabWrapper'));
+
+
+                            QUI.parse(Container).then(function () {
+                                return ControlsUtils.parse(Container);
+                            }).then(function () {
+                                var controls = QUI.Controls.getControlsInElement(Container),
+                                    project  = self.getAttribute('project');
+
+                                controls.each(function (Control) {
+                                    if (Control === self) {
+                                        return;
+                                    }
+
+                                    if ("setProject" in Control) {
+                                        Control.setProject(project);
+                                    }
+                                });
+
+                                Win.fireEvent('openAfterCreate', [Win]);
+
+                                moofx(Container).animate({
+                                    opacity: 1,
+                                    top    : 0
+                                }, {
+                                    duration: 250,
+                                    callback: function () {
+                                        resolve(Container);
+                                        Win.Loader.hide();
+                                    }
+                                });
+                            });
+                        }
+                    }
+                });
+
+                resolve(Dialog);
+            });
+        }
+    });
+});
diff --git a/bin/Controls/TextAndImageMultipleSettingsEntry.html b/bin/Controls/TextAndImageMultipleSettingsEntry.html
new file mode 100644
index 0000000..bde7da9
--- /dev/null
+++ b/bin/Controls/TextAndImageMultipleSettingsEntry.html
@@ -0,0 +1,50 @@
+<form name="quiqqer-bricks-textAndImageMultiple-settings-entry"
+      class="quiqqer-bricks-textAndImageMultiple-settings-entry-form"
+>
+    <table class="data-table data-table-flexbox">
+        <tbody>
+        <tr>
+            <td>
+                <label class="field-container">
+                    <span class="field-container-item">
+                        {{fieldIsDisabled}}
+                    </span>
+                    <span id="isDisabledWrapper" class="field-container-field"></span>
+                </label>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <label class="field-container">
+                    <span class="field-container-item">
+                        {{fieldImage}}
+                    </span>
+                    <input name="image" class="field-container-field media-image"/>
+                </label>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <label class="field-container">
+                    <span class="field-container-item">
+                        {{fieldTitle}}
+                    </span>
+                    <input name="title" class="field-container-field"/>
+                </label>
+            </td>
+        </tr>
+        <tr>
+            <td>
+                <label class="field-container">
+                    <span class="field-container-item">
+                        {{fieldDescription}}
+                    </span>
+                    <input name="description" class="field-container-field field-description"
+                           data-qui="controls/editors/Input"
+                    />
+                </label>
+            </td>
+        </tr>
+        </tbody>
+    </table>
+</form>
diff --git a/bricks.xml b/bricks.xml
index 98e11cf..4f8db38 100644
--- a/bricks.xml
+++ b/bricks.xml
@@ -1421,5 +1421,112 @@
             </settings>
         </brick>
 
+        <brick control="\QUI\Bricks\Controls\TextAndImageMultiple">
+            <title>
+                <locale group="quiqqer/bricks"
+                        var="brick.textAndImageMultiple.title"/>
+            </title>
+            <description>
+                <locale group="quiqqer/bricks"
+                        var="brick.textAndImageMultiple.description"/>
+            </description>
+
+            <settings>
+
+                <setting name="textPosition" type="select">
+                    <locale group="quiqqer/bricks"
+                            var="brick.textAndImageMultiple.textPosition"/>
+
+                    <option value="top">
+                        <locale group="quiqqer/bricks"
+                                var="brick.textAndImageMultiple.textPosition.top"/>
+                    </option>
+                    <option value="center">
+                        <locale group="quiqqer/bricks"
+                                var="brick.textAndImageMultiple.textPosition.center"/>
+                    </option>
+                    <option value="bottom">
+                        <locale group="quiqqer/bricks"
+                                var="brick.textAndImageMultiple.textPosition.bottom"/>
+                    </option>
+                </setting>
+
+                <setting name="imagePosition" type="select">
+                    <locale group="quiqqer/bricks"
+                            var="brick.textAndImageMultiple.imagePosition"/>
+
+                    <option value="imageLeft">
+                        <locale group="quiqqer/bricks"
+                                var="brick.textAndImageMultiple.imagePosition.imageLeft"/>
+                    </option>
+                    <option value="imageRight">
+                        <locale group="quiqqer/bricks"
+                                var="brick.textAndImageMultiple.imagePosition.imageRight"/>
+                    </option>
+                    <option value="imageLeftAlternately">
+                        <locale group="quiqqer/bricks"
+                                var="brick.textAndImageMultiple.imagePosition.imageLeftAlternately"/>
+                    </option>
+                    <option value="imageRightAlternately">
+                        <locale group="quiqqer/bricks"
+                                var="brick.textAndImageMultiple.imagePosition.imageRightAlternately"/>
+                    </option>
+                </setting>
+
+                <setting name="textRatio" type="select">
+                    <locale group="quiqqer/bricks"
+                            var="brick.textAndImage.textRatio"/>
+
+                    <option value="30">
+                        <locale group="quiqqer/bricks"
+                                var="brick.textAndImage.textRatio.30"/>
+                    </option>
+                    <option value="35">
+                        <locale group="quiqqer/bricks"
+                                var="brick.textAndImage.textRatio.35"/>
+                    </option>
+                    <option value="40">
+                        <locale group="quiqqer/bricks"
+                                var="brick.textAndImage.textRatio.40"/>
+                    </option>
+                    <option value="45">
+                        <locale group="quiqqer/bricks"
+                                var="brick.textAndImage.textRatio.45"/>
+                    </option>
+                    <option value="50" default="1">
+                        <locale group="quiqqer/bricks"
+                                var="brick.textAndImage.textRatio.50"/>
+                    </option>
+                    <option value="55">
+                        <locale group="quiqqer/bricks"
+                                var="brick.textAndImage.textRatio.55"/>
+                    </option>
+                    <option value="60">
+                        <locale group="quiqqer/bricks"
+                                var="brick.textAndImage.textRatio.60"/>
+                    </option>
+                    <option value="65">
+                        <locale group="quiqqer/bricks"
+                                var="brick.textAndImage.textRatio.65"/>
+                    </option>
+                    <option value="70">
+                        <locale group="quiqqer/bricks"
+                                var="brick.textAndImage.textRatio.70"/>
+                    </option>
+                </setting>
+                <setting name="maxImageWidth" type="number">
+                    <locale group="quiqqer/bricks"
+                            var="brick.textAndImage.maxImageWidth"/>
+                </setting>
+
+                <setting name="entries" type="hidden"
+                         data-qui="package/quiqqer/bricks/bin/Controls/TextAndImageMultipleSettings"
+                >
+                    <locale group="quiqqer/bricks" var="brick.textAndImageMultiple.settings.entries"/>
+                </setting>
+
+            </settings>
+        </brick>
+
     </bricks>
 </quiqqer>
diff --git a/locale.xml b/locale.xml
index 9d7cbfc..8014f65 100644
--- a/locale.xml
+++ b/locale.xml
@@ -1797,5 +1797,126 @@ M&ouml;chten Sie die Bausteine aus der Bausteinzone entfernen?</p>]]></de>
             <de><![CDATA[Text 70% - Bild 30%]]></de>
             <en><![CDATA[Text 70% - Image 30%]]></en>
         </locale>
+
+        <!-- Text and image mulitple -->
+        <locale name="brick.textAndImageMultiple.title">
+            <de><![CDATA[Bausteine: Text neben dem Bild - mehrere]]></de>
+            <en><![CDATA[Bricks: Text next to the image - multiple]]></en>
+        </locale>
+        <locale name="brick.textAndImageMultiple.description">
+            <de><![CDATA[Mehrere Abschnitte bestehend aus Text neben einem Bild.]]></de>
+            <en><![CDATA[Multiple sections composed of text placed next to an image.]]></en>
+        </locale>
+        <locale name="brick.textAndImageMultiple.textPosition">
+            <de><![CDATA[Text Position]]></de>
+            <en><![CDATA[Text position]]></en>
+        </locale>
+        <locale name="brick.textAndImageMultiple.textPosition.top">
+            <de><![CDATA[Oben]]></de>
+            <en><![CDATA[Top]]></en>
+        </locale>
+        <locale name="brick.textAndImageMultiple.textPosition.center">
+            <de><![CDATA[Zentriert]]></de>
+            <en><![CDATA[Center]]></en>
+        </locale>
+        <locale name="brick.textAndImageMultiple.textPosition.bottom">
+            <de><![CDATA[Unten]]></de>
+            <en><![CDATA[Bottom]]></en>
+        </locale>
+        <locale name="brick.textAndImageMultiple.imagePosition">
+            <de><![CDATA[Bildposition]]></de>
+            <en><![CDATA[Image position]]></en>
+        </locale>
+        <locale name="brick.textAndImageMultiple.imagePosition.imageLeft">
+            <de><![CDATA[Alle Bilder links]]></de>
+            <en><![CDATA[All images on the left]]></en>
+        </locale>
+        <locale name="brick.textAndImageMultiple.imagePosition.imageRight">
+            <de><![CDATA[Alle Bilder rechts]]></de>
+            <en><![CDATA[All images on the right]]></en>
+        </locale>
+        <locale name="brick.textAndImageMultiple.imagePosition.imageLeftAlternately">
+            <de><![CDATA[Abwechselnde Bilder - von links beginnend]]></de>
+            <en><![CDATA[Alternating images - starting from the left]]></en>
+        </locale>
+        <locale name="brick.textAndImageMultiple.imagePosition.imageRightAlternately">
+            <de><![CDATA[Abwechselnde Bilder - von rechts beginnend]]></de>
+            <en><![CDATA[Alternating images - starting from the right]]></en>
+        </locale>
+
+        <locale name="brick.textAndImage.textRatio">
+            <de><![CDATA[Text und Bild Platzverhältnis]]></de>
+            <en><![CDATA[Text ratio]]></en>
+        </locale>
+        <locale name="brick.textAndImage.textRatio.30">
+            <de><![CDATA[Text 30% - Bild 70%]]></de>
+            <en><![CDATA[Text 30% - Image 70%]]></en>
+        </locale>
+        <locale name="brick.textAndImage.textRatio.35">
+            <de><![CDATA[Text 35% - Bild 65%]]></de>
+            <en><![CDATA[Text 35% - Image 65%]]></en>
+        </locale>
+        <locale name="brick.textAndImage.textRatio.40">
+            <de><![CDATA[Text 40% - Bild 60%]]></de>
+            <en><![CDATA[Text 40% - Image 60%]]></en>
+        </locale>
+        <locale name="brick.textAndImage.textRatio.45">
+            <de><![CDATA[Text 45% - Bild 55%]]></de>
+            <en><![CDATA[Text 45% - Image 55%]]></en>
+        </locale>
+        <locale name="brick.textAndImage.textRatio.50">
+            <de><![CDATA[Text 50% - Bild 50% (standard)]]></de>
+            <en><![CDATA[Text 50% - Image 50% (default)]]></en>
+        </locale>
+        <locale name="brick.textAndImage.textRatio.55">
+            <de><![CDATA[Text 55% - Bild 45%]]></de>
+            <en><![CDATA[Text 55% - Image 45%]]></en>
+        </locale>
+        <locale name="brick.textAndImage.textRatio.60">
+            <de><![CDATA[Text 60% - Bild 40%]]></de>
+            <en><![CDATA[Text 60% - Image 40%]]></en>
+        </locale>
+        <locale name="brick.textAndImage.textRatio.65">
+            <de><![CDATA[Text 65% - Bild 35%]]></de>
+            <en><![CDATA[Text 65% - Image 35%]]></en>
+        </locale>
+        <locale name="brick.textAndImage.textRatio.70">
+            <de><![CDATA[Text 70% - Bild 30%]]></de>
+            <en><![CDATA[Text 70% - Image 30%]]></en>
+        </locale>
+
+        <locale name="brick.textAndImageMultiple.settings.entries">
+            <de><![CDATA[Entries]]></de>
+            <en><![CDATA[Entries]]></en>
+        </locale>
+        <locale name="quiqqer.bricks.textAndImageMultiple.create.isDisabled">
+            <de><![CDATA[Einträge deaktivieren]]></de>
+            <en><![CDATA[Deactivate entries]]></en>
+        </locale>
+        <locale name="quiqqer.bricks.textAndImageMultiple.create.isDisabled.short">
+            <de><![CDATA[Deaktiviert]]></de>
+            <en><![CDATA[Deactivated]]></en>
+        </locale>
+
+        <locale name="quiqqer.bricks.entires.adddialog.title">
+            <de><![CDATA[Eintrag hinzufügen]]></de>
+            <en><![CDATA[Add Entry]]></en>
+        </locale>
+        <locale name="quiqqer.bricks.entires.editdialog.title">
+            <de><![CDATA[Eintrag bearbeiten]]></de>
+            <en><![CDATA[Edit Entry]]></en>
+        </locale>
+        <locale name="quiqqer.bricks.entires.delete.title">
+            <de><![CDATA[Möchten Sie diesen Eintrag wirklich entfernen?]]></de>
+            <en><![CDATA[Do you really want to remove this entry?]]></en>
+        </locale>
+        <locale name="quiqqer.bricks.entires.delete.text">
+            <de><![CDATA[Möchten Sie diesen Eintrag wirklich entfernen?]]></de>
+            <en><![CDATA[Do you really want to remove this entry?]]></en>
+        </locale>
+        <locale name="quiqqer.bricks.entires.delete.information">
+            <de><![CDATA[Der Eintrag kann nicht wiederhergestellt werden]]></de>
+            <en><![CDATA[The entries can not be restored]]></en>
+        </locale>
     </groups>
 </locales>
diff --git a/src/QUI/Bricks/Controls/TextAndImageMultiple.css b/src/QUI/Bricks/Controls/TextAndImageMultiple.css
new file mode 100644
index 0000000..b5746c7
--- /dev/null
+++ b/src/QUI/Bricks/Controls/TextAndImageMultiple.css
@@ -0,0 +1,3 @@
+.test > .quiqqer-textImage {
+    margin-bottom: 100px;
+}
\ No newline at end of file
diff --git a/src/QUI/Bricks/Controls/TextAndImageMultiple.php b/src/QUI/Bricks/Controls/TextAndImageMultiple.php
new file mode 100644
index 0000000..d5809dd
--- /dev/null
+++ b/src/QUI/Bricks/Controls/TextAndImageMultiple.php
@@ -0,0 +1,106 @@
+<?php
+
+/**
+ * This file contains \QUI\Bricks\Controls\TextAndImageMultiple
+ */
+
+namespace QUI\Bricks\Controls;
+
+use QUI;
+
+/**
+ * Class TextAndImageMultiple
+ *
+ * @package quiqqer/bricks
+ */
+class TextAndImageMultiple extends QUI\Control
+{
+    /**
+     * constructor
+     *
+     * @param array $attributes
+     */
+    public function __construct($attributes = [])
+    {
+        // default options
+        $this->setAttributes([
+            'textPosition' => 'top',
+//            'imagePosition' => 'left',
+            'textRatio' => false,
+            'maxImageWidth'   => false,
+            'title' => false,
+            'text' => false,
+            'image' => false,
+        ]);
+
+        parent::__construct($attributes);
+
+        $this->addCSSFile(
+            dirname(__FILE__).'/TextAndImageMultiple.css'
+        );
+    }
+
+    /**
+     * (non-PHPdoc)
+     *
+     * @see \QUI\Control::create()
+     */
+    public function getBody()
+    {
+        $Engine = QUI::getTemplateManager()->getEngine();
+        $entries = json_decode($this->getAttribute('entries'), true);
+        $textRatio = $this->getAttribute('textRatio');
+        $imagePosition = $this->getAttribute('imagePosition');
+
+        $html = '';
+
+        foreach ($entries as $key=>$entry) {
+
+            if ($entry['isDisabled'] === 1) {
+                continue;
+            }
+
+            switch ($imagePosition) {
+                case "imageLeft":
+                    $imageOnLeft = true;
+                    break;
+
+                case "imageRight":
+                    $imageOnLeft = false;
+                    break;
+
+                case "imageLeftAlternately":
+                    $imageOnLeft = true;
+
+                    if($key % 2 !== 0) {
+                        $imageOnLeft = false;
+                    }
+                    break;
+
+                case "imageRightAlternately":
+                    $imageOnLeft = false;
+
+                    if($key % 2 !== 0) {
+                        $imageOnLeft = true;
+                    }
+                    break;
+            }
+
+            $TextAndImage = new QUI\Bricks\Controls\TextAndImage();
+            $TextAndImage->setAttribute('image', $entry['image']);
+            $TextAndImage->setAttribute('textImageRatio', $textRatio);
+            $TextAndImage->setAttribute('maxImageWidth', $this->getAttribute('maxImageWidth'));
+
+            $TextAndImage->setAttribute('imageOnLeft', $imageOnLeft);
+
+            $TextAndImage->setAttribute('content', $entry['text']);
+            $TextAndImage->addCSSClass('test');
+
+            $html .= $TextAndImage->create();
+
+            $this->addCSSFiles($TextAndImage->getCSSFiles());
+        }
+
+        return $html;
+    }
+}
-- 
GitLab