Как вертикально выровнять компонент GrapesJS без преобразования его родительского элемента во flexbox?CSS

Разбираемся в CSS
Ответить
Anonymous
 Как вертикально выровнять компонент GrapesJS без преобразования его родительского элемента во flexbox?

Сообщение Anonymous »

Проблема, с которой я столкнулся, связана с вертикальным выравниванием внутри компонента GrapesJS, но проблему можно воспроизвести даже без GrapesJS.

Вот небольшой работоспособный пример, показывающий точное поведение:

Код: Выделить всё

  width: 300px;
height: 150px;
border: 1px solid #ccc;
background: #fafafa;
">

Content



Top
Middle
Bottom

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;
},
});
В моей фактической настройке GrapesJS я создал собственный тип ввода Style Manager:

Код: Выделить всё

styleManager.addType("vertical-align-selector", { ... });
Применяется пользовательский элемент управления:

Код: Выделить всё

display: flex;
align-items: flex-start | center | flex-end;
Сейчас вертикальное выравнивание работает только в том случае, если родительский элемент становится гибким контейнером, поэтому я заставляю:

Код: Выделить всё

display: flex;
Я хочу знать:
Есть ли правильный способ вертикально выровнять компонент GrapesJS без преобразования его родительского элемента во flexbox?

Подробнее здесь: https://stackoverflow.com/questions/798 ... rent-to-fl
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «CSS»