Commit 36156e0f authored by jan.koester's avatar jan.koester
Browse files

easier paddings and margins

parent 1986d8e0
Loading
Loading
Loading
Loading
+61 −0
Changes for editor/html/css/editor.css: 61 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -460,6 +460,67 @@ body {
    background: var(--accent-hover);
}

/* Spacing box (padding/margin: linked top/right/bottom/left inputs) */
.spacing-box {
    display: flex;
    align-items: flex-end;
    gap: 6px;
}

.spacing-box-side {
    flex: 1;
    min-width: 0;
}

.spacing-box-side-label {
    display: block;
    font-size: 10px;
    color: var(--text-secondary);
    margin-bottom: 2px;
}

.spacing-box-side input {
    width: 100%;
    padding: 6px 6px;
    background: var(--bg-tertiary);
    color: var(--text-primary);
    border: 1px solid var(--border);
    border-radius: 3px;
    font-size: 12px;
    font-family: inherit;
}

.spacing-box-side input:focus {
    outline: none;
    border-color: var(--accent);
}

.spacing-box-lock {
    flex: none;
    width: 28px;
    height: 28px;
    background: var(--bg-tertiary);
    border: 1px solid var(--border);
    border-radius: 3px;
    color: var(--text-secondary);
    cursor: pointer;
    font-size: 13px;
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
}

.spacing-box-lock:hover {
    color: var(--text-primary);
}

.spacing-box-lock.active {
    color: var(--bg-primary);
    background: var(--accent);
    border-color: var(--accent);
}

/* Property panel tabs */
.prop-tabs {
    display: flex;
+9 −2
Changes for editor/html/js/canvas.js: 9 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -102,8 +102,15 @@ var Canvas = (function() {
        } else if (d.type === 'blogi-margin-change') {
            // The canvas already shows the dragged margin live; save it and
            // refresh so the properties sidebar (if open on this element)
            // picks up the new value.
            EditorApi.setProperties(d.uuid, { margin: d.value }).then(function() {
            // picks up the new value. Widgets model margin as four independent
            // fields (margin_top/right/bottom/left, see container.cpp's
            // addSpacingBox) rather than a single CSS shorthand string, so all
            // four sides are sent — widgets without a margin field just ignore
            // the unknown keys, same as before.
            EditorApi.setProperties(d.uuid, {
                margin_top: d.top, margin_right: d.right,
                margin_bottom: d.bottom, margin_left: d.left
            }).then(function() {
                if (onChangeCallback) onChangeCallback();
            }).catch(function(err) {
                console.error('Margin change failed to save:', err);
+68 −0
Changes for editor/html/js/properties.js: 68 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -372,6 +372,70 @@ var PropertiesPanel = (function() {
        group.appendChild(customPanel);
    }

    // Renders a padding/margin field as four top/right/bottom/left inputs plus
    // a lock toggle. Locking mirrors whatever side is being typed into across
    // the other three, so one value can drive all sides at once; unlocking
    // lets each side diverge again. Every side is a real named <input> so the
    // generic saveProperties() sweep (form.querySelectorAll('input,...'))
    // picks them up with no special-casing needed there.
    function _renderSpacingBox(group, field, properties) {
        var sides = ['top', 'right', 'bottom', 'left'];
        var sideLabels = {
            top: (typeof I18n !== 'undefined') ? I18n.t('I18N_SPACING_TOP', 'Oben') : 'Oben',
            right: (typeof I18n !== 'undefined') ? I18n.t('I18N_SPACING_RIGHT', 'Rechts') : 'Rechts',
            bottom: (typeof I18n !== 'undefined') ? I18n.t('I18N_SPACING_BOTTOM', 'Unten') : 'Unten',
            left: (typeof I18n !== 'undefined') ? I18n.t('I18N_SPACING_LEFT', 'Links') : 'Links'
        };

        var wrap = document.createElement('div');
        wrap.className = 'spacing-box';

        var locked = false;
        var inputs = {};

        var lockBtn = document.createElement('button');
        lockBtn.type = 'button';
        lockBtn.className = 'spacing-box-lock';
        lockBtn.title = 'Alle Seiten gemeinsam ändern';
        lockBtn.textContent = '🔓';
        lockBtn.addEventListener('click', function() {
            locked = !locked;
            lockBtn.classList.toggle('active', locked);
            lockBtn.textContent = locked ? '🔒' : '🔓';
        });

        sides.forEach(function(side) {
            var sideWrap = document.createElement('div');
            sideWrap.className = 'spacing-box-side spacing-box-side-' + side;

            var sideLabel = document.createElement('span');
            sideLabel.className = 'spacing-box-side-label';
            sideLabel.textContent = sideLabels[side];

            var input = document.createElement('input');
            input.type = 'text';
            input.name = field.keys[side];
            input.value = properties[field.keys[side]] || '';
            if (field.placeholder) input.placeholder = field.placeholder;

            input.addEventListener('input', function() {
                if (!locked) return;
                sides.forEach(function(otherSide) {
                    if (otherSide === side) return;
                    inputs[otherSide].value = input.value;
                });
            });

            inputs[side] = input;
            sideWrap.appendChild(sideLabel);
            sideWrap.appendChild(input);
            wrap.appendChild(sideWrap);
        });

        wrap.appendChild(lockBtn);
        group.appendChild(wrap);
    }

    function _renderFieldsInto(panel, fields, properties, form) {
        for (var i = 0; i < fields.length; i++) {
            var field = fields[i];
@@ -452,6 +516,10 @@ var PropertiesPanel = (function() {
                    _renderColorField(group, field, value);
                    panel.appendChild(group);
                    continue;
                } else if (field.type === 'spacing_box') {
                    _renderSpacingBox(group, field, properties);
                    panel.appendChild(group);
                    continue;
                } else if (field.type === 'html' || field.type === 'richtext') {
                    var rteWrap = document.createElement('div');
                    rteWrap.className = 'rte-wrap';
+1 −1
Changes for editor/src/webedit_api.cpp: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -1941,7 +1941,7 @@ std::string buildCanvasScript(const std::string &metaJson) {
        "var bottom=d.el.style.marginBottom||cs.marginBottom;"
        "var left=d.el.style.marginLeft||cs.marginLeft;"
        "window.parent.postMessage({type:'blogi-margin-change',uuid:d.uuid,"
        "value:[top,right,bottom,left].join(' ')},'*');"
        "top:top,right:right,bottom:bottom,left:left},'*');"
        "});"
        "})();</script>";
}
+102 −22
Changes for editor/widgets/article/article.cpp: 102 added lines, 22 removed lines.
Original line number Diff line number Diff line
@@ -47,8 +47,14 @@ extern "C" {
            std::string borderRadius = "";
            std::string borderStyle = "";
            std::string opacity = "";
            std::string padding = "";
            std::string margin = "";
            std::string paddingTop = "";
            std::string paddingRight = "";
            std::string paddingBottom = "";
            std::string paddingLeft = "";
            std::string marginTop = "";
            std::string marginRight = "";
            std::string marginBottom = "";
            std::string marginLeft = "";
            std::string maxWidth = "";
            std::string minHeight = "";
            std::string width = "";
@@ -183,6 +189,25 @@ extern "C" {
                    for (auto v : vals) json_object_array_add(a, json_object_new_string(v));
                    return a;
                };
                // Renders as four linked top/right/bottom/left inputs with a lock
                // toggle in the properties panel (see properties.js _renderSpacingBox)
                // instead of a single raw CSS shorthand string.
                auto addSpacingBox = [&](const char *key, const char *label, const char *target, const char *level = "expert") {
                    json_object *f = json_object_new_object();
                    json_object_object_add(f, "key", json_object_new_string(key));
                    json_object_object_add(f, "label", json_object_new_string(label));
                    json_object_object_add(f, "type", json_object_new_string("spacing_box"));
                    json_object_object_add(f, "placeholder", json_object_new_string("e.g. 10px"));
                    json_object_object_add(f, "target", json_object_new_string(target));
                    json_object_object_add(f, "level", json_object_new_string(level));
                    json_object *keys = json_object_new_object();
                    json_object_object_add(keys, "top", json_object_new_string((std::string(key) + "_top").c_str()));
                    json_object_object_add(keys, "right", json_object_new_string((std::string(key) + "_right").c_str()));
                    json_object_object_add(keys, "bottom", json_object_new_string((std::string(key) + "_bottom").c_str()));
                    json_object_object_add(keys, "left", json_object_new_string((std::string(key) + "_left").c_str()));
                    json_object_object_add(f, "keys", keys);
                    json_object_array_add(arr, f);
                };
                addField("background_color", "Background Color (Dark)", "color_var", nullptr, "desktop", nullptr, "simple");
                addField("l_background_color", "Background Color (Light)", "color_var", nullptr, "light", nullptr, "simple");
                addField("background_gradient_type", "Background Gradient", "select", nullptr, "desktop", mkOpts({"none","linear","radial"}), "simple");
@@ -196,8 +221,8 @@ extern "C" {
                addField("m_background_gradient_angle", "Mobile Gradient Angle", "text", "e.g. 135deg", "mobile");
                addField("m_background_gradient_custom", "Mobile Gradient Custom", "text", "e.g. linear-gradient(135deg, #ff0000, #0000ff)", "mobile");
                addField("css_class", "CSS Class", "text", "e.g. card");
                addField("padding", "Padding", "text", "e.g. 28px", "desktop");
                addField("margin", "Margin", "text", "e.g. 0 auto", "desktop");
                addSpacingBox("padding", "Padding", "desktop");
                addSpacingBox("margin", "Margin", "desktop");
                addField("max_width", "Max Width", "text", "e.g. 800px", "desktop");
                addField("min_height", "Min Height", "text", "e.g. 200px", "desktop");
                addField("width", "Width", "text", "e.g. 100%", "desktop");
@@ -240,8 +265,14 @@ extern "C" {
                element->SetAttribute("border_radius", borderRadius.c_str());
                element->SetAttribute("border_style", borderStyle.c_str());
                element->SetAttribute("opacity", opacity.c_str());
                element->SetAttribute("padding", padding.c_str());
                element->SetAttribute("margin", margin.c_str());
                element->SetAttribute("padding_top", paddingTop.c_str());
                element->SetAttribute("padding_right", paddingRight.c_str());
                element->SetAttribute("padding_bottom", paddingBottom.c_str());
                element->SetAttribute("padding_left", paddingLeft.c_str());
                element->SetAttribute("margin_top", marginTop.c_str());
                element->SetAttribute("margin_right", marginRight.c_str());
                element->SetAttribute("margin_bottom", marginBottom.c_str());
                element->SetAttribute("margin_left", marginLeft.c_str());
                element->SetAttribute("max_width", maxWidth.c_str());
                element->SetAttribute("min_height", minHeight.c_str());
                element->SetAttribute("width", width.c_str());
@@ -315,10 +346,31 @@ extern "C" {
                if (val) borderStyle = val;
                val = xml_data->Attribute("opacity");
                if (val) opacity = val;
                val = xml_data->Attribute("padding");
                if (val) padding = val;
                val = xml_data->Attribute("margin");
                if (val) margin = val;
                // Legacy migration: pre-split documents stored padding/margin as
                // a single CSS shorthand attribute. Split it into the per-side
                // fields below; the new per-side attributes (read next) take
                // precedence if the file was already saved in the new format.
                const char* legacy_padding = xml_data->Attribute("padding");
                if (legacy_padding) splitSpacingShorthand(legacy_padding, paddingTop, paddingRight, paddingBottom, paddingLeft);
                const char* legacy_margin = xml_data->Attribute("margin");
                if (legacy_margin) splitSpacingShorthand(legacy_margin, marginTop, marginRight, marginBottom, marginLeft);

                val = xml_data->Attribute("padding_top");
                if (val) paddingTop = val;
                val = xml_data->Attribute("padding_right");
                if (val) paddingRight = val;
                val = xml_data->Attribute("padding_bottom");
                if (val) paddingBottom = val;
                val = xml_data->Attribute("padding_left");
                if (val) paddingLeft = val;
                val = xml_data->Attribute("margin_top");
                if (val) marginTop = val;
                val = xml_data->Attribute("margin_right");
                if (val) marginRight = val;
                val = xml_data->Attribute("margin_bottom");
                if (val) marginBottom = val;
                val = xml_data->Attribute("margin_left");
                if (val) marginLeft = val;
                val = xml_data->Attribute("max_width");
                if (val) maxWidth = val;
                val = xml_data->Attribute("min_height");
@@ -405,8 +457,14 @@ extern "C" {
                json_object_object_add(obj, "border_radius", json_object_new_string(borderRadius.c_str()));
                json_object_object_add(obj, "border_style", json_object_new_string(borderStyle.c_str()));
                json_object_object_add(obj, "opacity", json_object_new_string(opacity.c_str()));
                json_object_object_add(obj, "padding", json_object_new_string(padding.c_str()));
                json_object_object_add(obj, "margin", json_object_new_string(margin.c_str()));
                json_object_object_add(obj, "padding_top", json_object_new_string(paddingTop.c_str()));
                json_object_object_add(obj, "padding_right", json_object_new_string(paddingRight.c_str()));
                json_object_object_add(obj, "padding_bottom", json_object_new_string(paddingBottom.c_str()));
                json_object_object_add(obj, "padding_left", json_object_new_string(paddingLeft.c_str()));
                json_object_object_add(obj, "margin_top", json_object_new_string(marginTop.c_str()));
                json_object_object_add(obj, "margin_right", json_object_new_string(marginRight.c_str()));
                json_object_object_add(obj, "margin_bottom", json_object_new_string(marginBottom.c_str()));
                json_object_object_add(obj, "margin_left", json_object_new_string(marginLeft.c_str()));
                json_object_object_add(obj, "max_width", json_object_new_string(maxWidth.c_str()));
                json_object_object_add(obj, "min_height", json_object_new_string(minHeight.c_str()));
                json_object_object_add(obj, "width", json_object_new_string(width.c_str()));
@@ -469,13 +527,37 @@ extern "C" {
                    val = json_object_get_string(val_obj);
                    if (val) opacity = val;
                }
                if (json_object_object_get_ex(obj, "padding", &val_obj)) {
                if (json_object_object_get_ex(obj, "padding_top", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) paddingTop = val;
                }
                if (json_object_object_get_ex(obj, "padding_right", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) paddingRight = val;
                }
                if (json_object_object_get_ex(obj, "padding_bottom", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) padding = val;
                    if (val) paddingBottom = val;
                }
                if (json_object_object_get_ex(obj, "margin", &val_obj)) {
                if (json_object_object_get_ex(obj, "padding_left", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) margin = val;
                    if (val) paddingLeft = val;
                }
                if (json_object_object_get_ex(obj, "margin_top", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) marginTop = val;
                }
                if (json_object_object_get_ex(obj, "margin_right", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) marginRight = val;
                }
                if (json_object_object_get_ex(obj, "margin_bottom", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) marginBottom = val;
                }
                if (json_object_object_get_ex(obj, "margin_left", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) marginLeft = val;
                }
                if (json_object_object_get_ex(obj, "max_width", &val_obj)) {
                    val = json_object_get_string(val_obj);
@@ -647,12 +729,10 @@ extern "C" {
                if (!opacity.empty()) {
                    style += " opacity: " + opacity + ";";
                }
                if (!padding.empty()) {
                    style += " padding: " + padding + ";";
                }
                if (!margin.empty()) {
                    style += " margin: " + margin + ";";
                }
                std::string paddingShorthand = joinSpacingShorthand(paddingTop, paddingRight, paddingBottom, paddingLeft);
                if (!paddingShorthand.empty()) style += " padding: " + paddingShorthand + ";";
                std::string marginShorthand = joinSpacingShorthand(marginTop, marginRight, marginBottom, marginLeft);
                if (!marginShorthand.empty()) style += " margin: " + marginShorthand + ";";
                if (!maxWidth.empty()) {
                    style += " max-width: " + maxWidth + ";";
                }
Loading