Commit 0046d05c authored by jan.koester's avatar jan.koester
Browse files

test

parent 3fdb1936
Loading
Loading
Loading
Loading
+76 −0
Changes for editor/html/css/editor.css: 76 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -1155,6 +1155,82 @@ dialog label {
    gap: 4px;
}

/* FAQ items editor */
.faq-items-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 8px;
}

.faq-item-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 8px;
    background: var(--bg-tertiary);
    border: 1px solid var(--border);
    border-radius: 4px;
}

.faq-item-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 11px;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.faq-item-remove {
    background: none;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    font-size: 13px;
    line-height: 1;
    padding: 2px 4px;
}

.faq-item-remove:hover {
    color: var(--danger);
}

.faq-item-question,
.faq-item-answer {
    width: 100%;
    padding: 6px 8px;
    background: var(--bg-secondary);
    color: var(--text-primary);
    border: 1px solid var(--border);
    border-radius: 3px;
    font-size: 13px;
    font-family: inherit;
    box-sizing: border-box;
}

.faq-item-answer {
    resize: vertical;
    min-height: 50px;
}

.faq-item-add {
    width: 100%;
    padding: 6px;
    background: var(--bg-tertiary);
    color: var(--accent);
    border: 1px dashed var(--accent);
    border-radius: 4px;
    cursor: pointer;
    font-size: 12px;
    font-weight: 500;
}

.faq-item-add:hover {
    background: var(--bg-hover);
}

.media-browse-field input {
    flex: 1;
}
+92 −0
Changes for editor/html/js/properties.js: 92 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -698,6 +698,92 @@ var PropertiesPanel = (function() {
                    group.appendChild(wrapper);
                    panel.appendChild(group);
                    continue;
                } else if (field.type === 'faq_items') {
                    var faqWrap = document.createElement('div');
                    faqWrap.className = 'faq-items-editor';

                    var faqHidden = document.createElement('input');
                    faqHidden.type = 'hidden';
                    faqHidden.name = field.key;
                    faqHidden.setAttribute('data-json-array', 'true');

                    var faqList = Array.isArray(value) ? value.slice() : [];
                    var faqListEl = document.createElement('div');
                    faqListEl.className = 'faq-items-list';

                    function faqSyncHidden() {
                        faqHidden.value = JSON.stringify(faqList);
                        faqHidden.dispatchEvent(new Event('change', { bubbles: true }));
                    }

                    function faqRenderList() {
                        faqListEl.innerHTML = '';
                        faqList.forEach(function(item, idx) {
                            var card = document.createElement('div');
                            card.className = 'faq-item-card';

                            var cardHeader = document.createElement('div');
                            cardHeader.className = 'faq-item-header';
                            cardHeader.textContent = 'Frage ' + (idx + 1);

                            var removeBtn = document.createElement('button');
                            removeBtn.type = 'button';
                            removeBtn.className = 'faq-item-remove';
                            removeBtn.textContent = '✕';
                            removeBtn.title = 'Entfernen';
                            removeBtn.addEventListener('click', function() {
                                faqList.splice(idx, 1);
                                faqSyncHidden();
                                faqRenderList();
                            });
                            cardHeader.appendChild(removeBtn);

                            var qInput = document.createElement('input');
                            qInput.type = 'text';
                            qInput.placeholder = 'Frage';
                            qInput.className = 'faq-item-question';
                            qInput.value = item.question || '';
                            qInput.addEventListener('input', function() {
                                faqList[idx].question = qInput.value;
                                faqSyncHidden();
                            });

                            var aInput = document.createElement('textarea');
                            aInput.placeholder = 'Antwort';
                            aInput.className = 'faq-item-answer';
                            aInput.rows = 3;
                            aInput.value = item.answer || '';
                            aInput.addEventListener('input', function() {
                                faqList[idx].answer = aInput.value;
                                faqSyncHidden();
                            });

                            card.appendChild(cardHeader);
                            card.appendChild(qInput);
                            card.appendChild(aInput);
                            faqListEl.appendChild(card);
                        });
                    }

                    faqRenderList();
                    faqSyncHidden();

                    var faqAddBtn = document.createElement('button');
                    faqAddBtn.type = 'button';
                    faqAddBtn.className = 'faq-item-add';
                    faqAddBtn.textContent = '+ Frage hinzufügen';
                    faqAddBtn.addEventListener('click', function() {
                        faqList.push({ question: '', answer: '' });
                        faqSyncHidden();
                        faqRenderList();
                    });

                    faqWrap.appendChild(faqListEl);
                    faqWrap.appendChild(faqAddBtn);
                    faqWrap.appendChild(faqHidden);
                    group.appendChild(faqWrap);
                    panel.appendChild(group);
                    continue;
                } else {
                    input = document.createElement('input');
                    input.type = 'text';
@@ -730,6 +816,12 @@ var PropertiesPanel = (function() {
            var input = inputs[i];
            if (input.type === 'checkbox') {
                properties[input.name] = input.checked;
            } else if (input.getAttribute('data-json-array') === 'true') {
                try {
                    properties[input.name] = JSON.parse(input.value || '[]');
                } catch (e) {
                    properties[input.name] = [];
                }
            } else {
                properties[input.name] = input.value;
            }
+35 −2
Changes for editor/widgets/faq/faq.cpp: 35 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ extern "C" {
            std::string titleColor        = "var(--blogi-text)";
            std::string padding           = "0";
            std::string maxWidth          = "";
            std::string columns           = "1";

            // Question / answer colors (dark mode defaults)
            std::string questionBgColor   = "var(--blogi-surface)";
@@ -203,6 +204,11 @@ extern "C" {
                    if (options) json_object_object_add(f, "options", options);
                    json_object_array_add(arr, f);
                };
                auto mkOpts = [](std::initializer_list<const char*> vals) {
                    json_object *a = json_object_new_array();
                    for (auto v : vals) json_object_array_add(a, json_object_new_string(v));
                    return a;
                };

                // Container
                addField("title",             "Section Title",        "text",      "e.g. FAQ");
@@ -211,6 +217,7 @@ extern "C" {
                addField("l_title_color",     "Title Color (Light)",  "color_var", nullptr,   "light");
                addField("padding",           "Padding",              "text",      "0",       "desktop");
                addField("max_width",         "Max Width",            "text",      "e.g. 800px");
                addField("columns",           "Spalten",              "select",    nullptr,   "desktop", mkOpts({"1","2","3","4"}));

                // Question row styling
                addField("question_bg_color",   "Question Background (Dark)",  "color_var");
@@ -282,6 +289,7 @@ extern "C" {
                element->SetAttribute("title_color",          titleColor.c_str());
                element->SetAttribute("padding",              padding.c_str());
                element->SetAttribute("max_width",            maxWidth.c_str());
                element->SetAttribute("columns",              columns.c_str());

                element->SetAttribute("question_bg_color",    questionBgColor.c_str());
                element->SetAttribute("question_text_color",  questionTextColor.c_str());
@@ -337,6 +345,7 @@ extern "C" {
                val = xml_data->Attribute("title_color");          if (val) titleColor = val;
                val = xml_data->Attribute("padding");              if (val) padding = val;
                val = xml_data->Attribute("max_width");            if (val) maxWidth = val;
                val = xml_data->Attribute("columns");              if (val) columns = val;

                val = xml_data->Attribute("question_bg_color");    if (val) questionBgColor = val;
                val = xml_data->Attribute("question_text_color");  if (val) questionTextColor = val;
@@ -395,6 +404,7 @@ extern "C" {
                json_object_object_add(obj, "title_color",       json_object_new_string(titleColor.c_str()));
                json_object_object_add(obj, "padding",           json_object_new_string(padding.c_str()));
                json_object_object_add(obj, "max_width",         json_object_new_string(maxWidth.c_str()));
                json_object_object_add(obj, "columns",           json_object_new_string(columns.c_str()));

                json_object_object_add(obj, "question_bg_color",    json_object_new_string(questionBgColor.c_str()));
                json_object_object_add(obj, "question_text_color",  json_object_new_string(questionTextColor.c_str()));
@@ -477,6 +487,7 @@ extern "C" {
                strNonEmpty("title_color",       titleColor);
                str("padding",                   padding);
                str("max_width",                 maxWidth);
                strNonEmpty("columns",            columns);

                strNonEmpty("question_bg_color",    questionBgColor);
                strNonEmpty("question_text_color",  questionTextColor);
@@ -558,6 +569,12 @@ extern "C" {
                    return !v.empty() && v != "none";
                };

                int colsN = 1;
                try { colsN = std::stoi(columns); } catch (...) { colsN = 1; }
                if (colsN < 1) colsN = 1;
                if (colsN > 4) colsN = 4;
                bool gridMode = colsN > 1;

                // Outer container
                libhtmlpp::HtmlElement outer("div");
                outer.setAttribute("class", (rc + "-faq").c_str());
@@ -584,6 +601,14 @@ extern "C" {
                }

                // FAQ items
                libhtmlpp::HtmlElement itemsWrap("div");
                itemsWrap.setAttribute("class", (rc + "-items").c_str());
                if (gridMode) {
                    std::string gridStyle = "display: grid; grid-template-columns: repeat(" +
                        std::to_string(colsN) + ", 1fr); gap: 16px;";
                    itemsWrap.setAttribute("style", gridStyle.c_str());
                }

                for (size_t i = 0; i < items.size(); ++i) {
                    const auto &item = items[i];
                    std::string contentId = rc + "-ans-" + std::to_string(i);
@@ -595,6 +620,10 @@ extern "C" {
                    itemEl.setAttribute("itemtype", "https://schema.org/Question");

                    std::string itemStyle;
                    if (gridMode) {
                        if (validCss(borderColor))  itemStyle += "border: 1px solid " + borderColor + ";";
                        if (validCss(borderRadius)) itemStyle += " border-radius: " + borderRadius + ";";
                    } else {
                        if (i > 0 && validCss(borderColor))
                            itemStyle += "border-top: 1px solid " + borderColor + ";";
                        if (i == 0 && validCss(borderColor))
@@ -603,6 +632,7 @@ extern "C" {
                            itemStyle += " border-radius: " + borderRadius + " " + borderRadius + " 0 0;";
                        if (validCss(borderRadius) && i + 1 == items.size())
                            itemStyle += " border-radius: 0 0 " + borderRadius + " " + borderRadius + ";";
                    }
                    itemStyle += " overflow: hidden;";
                    if (!itemStyle.empty()) itemEl.setAttribute("style", itemStyle.c_str());

@@ -669,9 +699,11 @@ extern "C" {
                    aDiv.appendChild(ansText);
                    itemEl.appendChild(aDiv);

                    outer.appendChild(itemEl);
                    itemsWrap.appendChild(itemEl);
                }

                outer.appendChild(itemsWrap);

                el.appendChild(outer);

                // --- Responsive & Light mode CSS ---
@@ -688,11 +720,12 @@ extern "C" {
                if (validCss(m_answerFontSize))   mobileACSS += " font-size: " + m_answerFontSize + " !important;";
                if (validCss(m_padding))          mobileWrapCSS += " padding: " + m_padding + " !important;";

                if (!mobileWrapCSS.empty() || !mobileQCSS.empty() || !mobileACSS.empty()) {
                if (!mobileWrapCSS.empty() || !mobileQCSS.empty() || !mobileACSS.empty() || gridMode) {
                    mediaCSS += "@media (max-width:768px){";
                    if (!mobileWrapCSS.empty()) mediaCSS += "." + rc + "-faq{" + mobileWrapCSS + "}";
                    if (!mobileQCSS.empty())    mediaCSS += "." + rc + "-q{" + mobileQCSS + "}";
                    if (!mobileACSS.empty())    mediaCSS += "." + rc + "-a{" + mobileACSS + "}";
                    if (gridMode)               mediaCSS += "." + rc + "-items{grid-template-columns:1fr !important;}";
                    mediaCSS += "}";
                }