Quantcast
Channel: Active questions tagged flexbox - Stack Overflow
Viewing all articles
Browse latest Browse all 1675

How to vertically align a GrapesJS component without converting its parent to flexbox? [duplicate]

$
0
0

The issue I’m facing is about vertical alignment inside a GrapesJS component, but the problem can be reproduced even without GrapesJS.
Here is a small, runnable example showing the exact behavior:

<div id="parent" style="  width: 300px;  height: 150px;  border: 1px solid #ccc;  background: #fafafa;"><div id="child" style="    background: #eee;    padding: 20px;">    Content</div></div><div style="margin-top: 10px;"><button data-value="flex-start">Top</button><button data-value="center">Middle</button><button data-value="flex-end">Bottom</button></div>document.querySelectorAll("button").forEach((btn) => {  btn.addEventListener("click", () => {    const parent = document.getElementById("parent");    // In GrapesJS this is parent.addStyle(...)    if (parent.style.display !== "flex") {      parent.style.display = "flex";      parent.style.flexDirection = "row";    }    parent.style.alignItems = btn.dataset.value;  });});styleManager.addType("horizontal-align-selector", {    create(_args?: any): HTMLElement {      const el = document.createElement("div");      el.className = "horizontal-align-selector-container";      el.style.cssText ="display: flex; gap: 8px; padding: 10px 0; flex-wrap: wrap;";      const options: EnhancedRadioOption[] = [        { value: "left", title: "Left", className: "fa fa-align-left" },        {          value: "center",          title: "Center",          className: "fa fa-align-center",        },        { value: "right", title: "Right", className: "fa fa-align-right" },      ];      options.forEach((opt) => {        const button = document.createElement("button");        button.type = "button";        button.className = "horizontal-align-btn";        button.dataset.value = opt.value;        button.title = opt.title || "";        if (opt.className) {          const icon = document.createElement("i");          icon.className = opt.className;          button.appendChild(icon);        } else {          button.textContent = opt.title || opt.value;        }        button.style.cssText = `          min-width: 48px;          height: 40px;          padding: 10px 12px;          border: 2px solid #e5e7eb;          background: #ffffff;          cursor: pointer;          transition: all 0.2s ease;          display: flex;          align-items: center;          justify-content: center;          font-weight: 600;          font-size: 14px;          color: #6b7280;          border-radius: 7px;        `;        button.addEventListener("click", () => {          const selected = gjsEditor.getSelected();          if (!selected) return;          const parent = selected.parent();          if (parent) {            const parentStyles = parent.getStyle();            const parentDisplay = parentStyles.display;            if (parentDisplay !== "flex" && parentDisplay !== "inline-flex") {              parent.addStyle({                display: "flex",              });            }            let justifyContentValue = "flex-start";            if (opt.value === "center") {              justifyContentValue = "center";            } else if (opt.value === "right") {              justifyContentValue = "flex-end";            }            parent.addStyle({"justify-content": justifyContentValue,            });          }          updateButtonStates();        });        el.appendChild(button);      });      const updateButtonStates = (): void => {        const selected = gjsEditor.getSelected();        if (!selected) return;        const parent = selected.parent();        if (!parent) return;        const parentStyles = parent.getStyle();        const currentValue = parentStyles["justify-content"] || "flex-start";        el.querySelectorAll(".horizontal-align-btn").forEach((button) => {          const btnEl = button as HTMLElement;          const btnValue = btnEl.dataset.value; // left, center, right          let isActive = false;          if (btnValue === 'left'&& (currentValue === 'flex-start' || !currentValue)) {              isActive = true;          } else if (btnValue === 'center'&& currentValue === 'center') {              isActive = true;          } else if (btnValue === 'right'&& currentValue === 'flex-end') {              isActive = true;          }          if (isActive) {            btnEl.style.background = "#6366f1";            btnEl.style.color = "white";            btnEl.style.borderColor = "#6366f1";            btnEl.style.boxShadow = "0 0 0 3px rgba(99, 102, 241, 0.1)";          } else {            btnEl.style.background = "white";            btnEl.style.color = "#6b7280";            btnEl.style.borderColor = "#e5e7eb";            btnEl.style.boxShadow = "none";          }        });      };      setTimeout(updateButtonStates, 0);      gjsEditor.on("component:selected", updateButtonStates);      gjsEditor.on("styleable:change", updateButtonStates);      return el;    },  });  // Vertical Align Selector Type  styleManager.addType("vertical-align-selector", {    create(_args?: any): HTMLElement {      const el = document.createElement("div");      el.className = "vertical-align-selector-container";      el.style.cssText ="display: flex; gap: 8px; padding: 10px 0; flex-wrap: wrap;";      const options: EnhancedRadioOption[] = [        { value: "flex-start", title: "Top", className: "fa fa-arrow-up" },        { value: "center", title: "Middle", className: "fa fa-arrows-v" },        { value: "flex-end", title: "Bottom", className: "fa fa-arrow-down" },      ];      options.forEach((opt) => {        const button = document.createElement("button");        button.type = "button";        button.className = "vertical-align-btn";        button.dataset.value = opt.value;        button.title = opt.title || "";        if (opt.className) {          const icon = document.createElement("i");          icon.className = opt.className;          button.appendChild(icon);        } else {          button.textContent = opt.title || opt.value;        }        button.style.cssText = `          min-width: 48px;          height: 40px;          padding: 10px 12px;          border: 2px solid #e5e7eb;          background: #ffffff;          cursor: pointer;          transition: all 0.2s ease;          display: flex;          align-items: center;          justify-content: center;          font-weight: 600;          font-size: 14px;          color: #6b7280;          border-radius: 7px;        `;        button.addEventListener("click", () => {          const selected = gjsEditor.getSelected();          if (!selected) return;          const parent = selected.parent();          if (parent) {            const parentStyles = parent.getStyle();            const parentDisplay = parentStyles.display;            if (parentDisplay !== "flex" && parentDisplay !== "inline-flex") {              parent.addStyle({                display: "flex","flex-direction": "row",              });            }             parent.addStyle({"align-items": opt.value,            });          }          updateButtonStates();        });        el.appendChild(button);      });      const updateButtonStates = (): void => {        const selected = gjsEditor.getSelected();        if (!selected) return;        const parent = selected.parent();        if(!parent) return;        const styles = parent.getStyle();        const currentValue = styles["align-items"] || "flex-start";        el.querySelectorAll(".vertical-align-btn").forEach((button) => {          const btnValue = (button as HTMLElement).dataset.value;          const isActive =            currentValue === btnValue ||            (btnValue === "flex-start" &&              (!currentValue ||                currentValue === "normal" ||                currentValue === "stretch"));          const btnEl = button as HTMLElement;          if (isActive) {            btnEl.style.background = "#6366f1";            btnEl.style.color = "white";            btnEl.style.borderColor = "#6366f1";            btnEl.style.boxShadow = "0 0 0 3px rgba(99, 102, 241, 0.1)";          } else {            btnEl.style.background = "white";            btnEl.style.color = "#6b7280";            btnEl.style.borderColor = "#e5e7eb";            btnEl.style.boxShadow = "none";          }        });      };      setTimeout(updateButtonStates, 0);      gjsEditor.on("component:selected", updateButtonStates);      gjsEditor.on("styleable:change", updateButtonStates);      return el;    },  });

In my actual GrapesJS setup, I created a custom Style Manager input type:

styleManager.addType("vertical-align-selector", { ... });

The custom control applies:

display: flex;align-items: flex-start | center | flex-end;

Right now, vertical alignment only works if the parent becomes a flex container, so I’m forcing:

display: flex;

I want to know:

Is there a proper way to vertically align a GrapesJS component without converting its parent to flexbox?


Viewing all articles
Browse latest Browse all 1675

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>