스타일 수정
This commit is contained in:
@@ -26,7 +26,7 @@ import {
|
||||
buildVideoEmbedUrl,
|
||||
computeVideoPublicTokens,
|
||||
} from "@/features/editor/utils/videoHelpers";
|
||||
import { computeButtonPublicTokens } from "@/features/editor/utils/buttonHelpers";
|
||||
import { computeButtonPublicTokens, computeButtonPbTokens } from "@/features/editor/utils/buttonHelpers";
|
||||
import { computeTextPublicTokens } from "@/features/editor/utils/textHelpers";
|
||||
import { computeDividerPublicTokens } from "@/features/editor/utils/dividerHelpers";
|
||||
import { computeImagePublicTokens } from "@/features/editor/utils/imageHelpers";
|
||||
@@ -50,8 +50,12 @@ export function getSectionLayoutConfig(props: SectionBlockProps) {
|
||||
? "bg-sky-900"
|
||||
: "bg-slate-950";
|
||||
|
||||
// 섹션 수직 패딩: builder.css 의 pb-section-py-sm/md/lg (1rem/1.5rem/2.5rem)에 대응
|
||||
// - sm → 1rem → py-4
|
||||
// - md → 1.5rem → py-6
|
||||
// - lg → 2.5rem → py-10
|
||||
const paddingYClass =
|
||||
props.paddingY === "sm" ? "py-8" : props.paddingY === "lg" ? "py-20" : "py-12";
|
||||
props.paddingY === "sm" ? "py-4" : props.paddingY === "lg" ? "py-10" : "py-6";
|
||||
|
||||
const maxWidthClass =
|
||||
props.maxWidthMode === "narrow"
|
||||
@@ -302,16 +306,40 @@ export function PublicPageRenderer({ blocks }: PublicPageRendererProps) {
|
||||
|
||||
if (block.type === "button") {
|
||||
const props = block.props as ButtonBlockProps;
|
||||
const tokens = computeButtonPublicTokens(props);
|
||||
const buttonStyle: CSSProperties = { ...tokens.styleOverrides };
|
||||
|
||||
const pbTokens = computeButtonPbTokens({
|
||||
align: props.align ?? "left",
|
||||
size: props.size ?? "md",
|
||||
variant: props.variant ?? "solid",
|
||||
colorPalette: props.colorPalette ?? "primary",
|
||||
borderRadius: props.borderRadius ?? "md",
|
||||
fullWidth: props.fullWidth ?? false,
|
||||
widthMode: props.widthMode ?? undefined,
|
||||
widthPx: props.widthPx ?? undefined,
|
||||
paddingX: props.paddingX ?? undefined,
|
||||
paddingY: props.paddingY ?? undefined,
|
||||
fillColorCustom: props.fillColorCustom ?? undefined,
|
||||
strokeColorCustom: props.strokeColorCustom ?? undefined,
|
||||
textColorCustom: props.textColorCustom ?? undefined,
|
||||
});
|
||||
|
||||
const publicTokens = computeButtonPublicTokens(props);
|
||||
const buttonStyle: CSSProperties = { ...publicTokens.styleOverrides };
|
||||
|
||||
const wrapperClass = pbTokens.alignClass;
|
||||
const buttonClassName = [
|
||||
"pb-btn-base",
|
||||
pbTokens.sizeClass,
|
||||
pbTokens.variantClass,
|
||||
pbTokens.radiusClass,
|
||||
publicTokens.widthClass,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
|
||||
return (
|
||||
<div key={block.id} className={tokens.alignClass}>
|
||||
<a
|
||||
href={props.href}
|
||||
className={`inline-flex items-center justify-center text-sm font-medium hover:bg-sky-500 transition-colors ${tokens.widthClass}`}
|
||||
style={buttonStyle}
|
||||
>
|
||||
<div key={block.id} className={wrapperClass}>
|
||||
<a href={props.href} className={buttonClassName} style={buttonStyle}>
|
||||
<span className="whitespace-pre-wrap">{props.label}</span>
|
||||
</a>
|
||||
</div>
|
||||
@@ -326,13 +354,13 @@ export function PublicPageRenderer({ blocks }: PublicPageRendererProps) {
|
||||
<div
|
||||
key={block.id}
|
||||
data-testid="preview-divider"
|
||||
className={`flex ${tokens.alignClass}`}
|
||||
className={`flex w-full ${tokens.alignClass}`}
|
||||
style={{ marginTop: tokens.wrapperMarginEm, marginBottom: tokens.wrapperMarginEm }}
|
||||
>
|
||||
<div style={tokens.innerStyle}>
|
||||
<div className={tokens.innerWidthClass} style={tokens.innerStyle}>
|
||||
<div
|
||||
data-testid="preview-divider-line"
|
||||
className={`${tokens.thicknessClass} ${tokens.innerWidthClass}`}
|
||||
className={tokens.thicknessClass}
|
||||
style={tokens.lineStyle}
|
||||
/>
|
||||
</div>
|
||||
@@ -367,27 +395,17 @@ export function PublicPageRenderer({ blocks }: PublicPageRendererProps) {
|
||||
|
||||
return (
|
||||
<ListTag
|
||||
className={tokens.alignClass}
|
||||
className={`pb-list ${tokens.alignClass}`}
|
||||
style={{
|
||||
...tokens.listStyle,
|
||||
listStyleType:
|
||||
tokens.bulletStyle === "none"
|
||||
? "none"
|
||||
: tokens.bulletStyle,
|
||||
paddingLeft: 12 + level * 8,
|
||||
}}
|
||||
>
|
||||
{nodes.map((node, index) => (
|
||||
<li
|
||||
key={node.id}
|
||||
style={
|
||||
index < nodes.length - 1
|
||||
? {
|
||||
marginBottom: `${tokens.gapEm}em`,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{nodes.map((node) => (
|
||||
<li key={node.id}>
|
||||
{node.text}
|
||||
{node.children && node.children.length > 0 && renderListNodes(node.children, level + 1)}
|
||||
</li>
|
||||
|
||||
@@ -64,17 +64,19 @@ export function computeButtonPbTokens(input: ButtonStyleInput): ButtonPbTokens {
|
||||
|
||||
const widthMode = input.widthMode ?? (input.fullWidth ? "full" : "auto");
|
||||
if (widthMode === "fixed" && typeof input.widthPx === "number" && input.widthPx > 0) {
|
||||
inlineStyles.push(`width:${input.widthPx}px`);
|
||||
inlineStyles.push(`width:${pxToEm(input.widthPx)}`);
|
||||
}
|
||||
|
||||
if (typeof input.paddingX === "number") {
|
||||
inlineStyles.push(`padding-left:${input.paddingX}px`);
|
||||
inlineStyles.push(`padding-right:${input.paddingX}px`);
|
||||
const paddingInline = pxToEm(input.paddingX);
|
||||
inlineStyles.push(`padding-left:${paddingInline}`);
|
||||
inlineStyles.push(`padding-right:${paddingInline}`);
|
||||
}
|
||||
|
||||
if (typeof input.paddingY === "number") {
|
||||
inlineStyles.push(`padding-top:${input.paddingY}px`);
|
||||
inlineStyles.push(`padding-bottom:${input.paddingY}px`);
|
||||
const paddingBlock = pxToEm(input.paddingY);
|
||||
inlineStyles.push(`padding-top:${paddingBlock}`);
|
||||
inlineStyles.push(`padding-bottom:${paddingBlock}`);
|
||||
}
|
||||
|
||||
if (input.fillColorCustom && input.fillColorCustom.trim() !== "") {
|
||||
|
||||
@@ -19,8 +19,9 @@ export const computeDividerExportTokens = (
|
||||
const colorRaw = typeof props.colorHex === "string" ? props.colorHex.trim() : "";
|
||||
const color = colorRaw !== "" ? colorRaw : "#475569";
|
||||
const marginY = typeof props.marginYPx === "number" && props.marginYPx > 0 ? props.marginYPx : 16;
|
||||
const marginEm = pxToEm(marginY);
|
||||
|
||||
const style = `border:0;border-bottom:${thickness} solid ${escapeAttr(color)};margin:${marginY}px 0;`;
|
||||
const style = `border:0;border-bottom:${thickness} solid ${escapeAttr(color)};margin:${marginEm} 0;`;
|
||||
|
||||
return { style };
|
||||
};
|
||||
|
||||
@@ -84,20 +84,21 @@ export const computeFormInputExportTokens = (
|
||||
}
|
||||
|
||||
if (typeof props.paddingX === "number" && props.paddingX >= 0) {
|
||||
const px = props.paddingX;
|
||||
inputStyleParts.push(`padding-left:${px}px`);
|
||||
inputStyleParts.push(`padding-right:${px}px`);
|
||||
const em = props.paddingX / 16;
|
||||
inputStyleParts.push(`padding-left:${em}em`);
|
||||
inputStyleParts.push(`padding-right:${em}em`);
|
||||
}
|
||||
|
||||
if (typeof props.paddingY === "number" && props.paddingY >= 0) {
|
||||
const py = props.paddingY;
|
||||
inputStyleParts.push(`padding-top:${py}px`);
|
||||
inputStyleParts.push(`padding-bottom:${py}px`);
|
||||
const em = props.paddingY / 16;
|
||||
inputStyleParts.push(`padding-top:${em}em`);
|
||||
inputStyleParts.push(`padding-bottom:${em}em`);
|
||||
}
|
||||
|
||||
const widthMode = computeWidthMode({ widthMode: props.widthMode, fullWidth: props.fullWidth });
|
||||
if (widthMode === "fixed" && typeof props.widthPx === "number" && props.widthPx > 0) {
|
||||
inputStyleParts.push(`width:${props.widthPx}px`);
|
||||
const em = props.widthPx / 16;
|
||||
inputStyleParts.push(`width:${em}em`);
|
||||
}
|
||||
|
||||
const radiusPx = computeRadiusPx(props.borderRadius);
|
||||
@@ -127,19 +128,20 @@ export const computeFormSelectExportTokens = (
|
||||
|
||||
const widthMode = computeWidthMode({ widthMode: props.widthMode, fullWidth: props.fullWidth });
|
||||
if (widthMode === "fixed" && typeof props.widthPx === "number" && props.widthPx > 0) {
|
||||
selectStyleParts.push(`width:${props.widthPx}px`);
|
||||
const em = props.widthPx / 16;
|
||||
selectStyleParts.push(`width:${em}em`);
|
||||
}
|
||||
|
||||
if (typeof props.paddingX === "number" && props.paddingX >= 0) {
|
||||
const px = props.paddingX;
|
||||
selectStyleParts.push(`padding-left:${px}px`);
|
||||
selectStyleParts.push(`padding-right:${px}px`);
|
||||
const em = props.paddingX / 16;
|
||||
selectStyleParts.push(`padding-left:${em}em`);
|
||||
selectStyleParts.push(`padding-right:${em}em`);
|
||||
}
|
||||
|
||||
if (typeof props.paddingY === "number" && props.paddingY >= 0) {
|
||||
const py = props.paddingY;
|
||||
selectStyleParts.push(`padding-top:${py}px`);
|
||||
selectStyleParts.push(`padding-bottom:${py}px`);
|
||||
const em = props.paddingY / 16;
|
||||
selectStyleParts.push(`padding-top:${em}em`);
|
||||
selectStyleParts.push(`padding-bottom:${em}em`);
|
||||
}
|
||||
|
||||
if (typeof props.fillColorCustom === "string" && props.fillColorCustom.trim() !== "") {
|
||||
@@ -176,15 +178,15 @@ const computeOptionGroupExportTokensBase = (
|
||||
}
|
||||
|
||||
if (typeof props.paddingX === "number" && props.paddingX >= 0) {
|
||||
const px = props.paddingX;
|
||||
optionStyleParts.push(`padding-left:${px}px`);
|
||||
optionStyleParts.push(`padding-right:${px}px`);
|
||||
const em = props.paddingX / 16;
|
||||
optionStyleParts.push(`padding-left:${em}em`);
|
||||
optionStyleParts.push(`padding-right:${em}em`);
|
||||
}
|
||||
|
||||
if (typeof props.paddingY === "number" && props.paddingY >= 0) {
|
||||
const py = props.paddingY;
|
||||
optionStyleParts.push(`padding-top:${py}px`);
|
||||
optionStyleParts.push(`padding-bottom:${py}px`);
|
||||
const em = props.paddingY / 16;
|
||||
optionStyleParts.push(`padding-top:${em}em`);
|
||||
optionStyleParts.push(`padding-bottom:${em}em`);
|
||||
}
|
||||
|
||||
if (typeof props.fillColorCustom === "string" && props.fillColorCustom.trim() !== "") {
|
||||
|
||||
@@ -27,7 +27,8 @@ export function computeImageExportStyles(input: ImageExportStyleInput): ImageExp
|
||||
|
||||
const widthMode: ImageWidthMode = input.widthMode ?? "auto";
|
||||
if (widthMode === "fixed" && typeof input.widthPx === "number" && input.widthPx > 0) {
|
||||
imgStyleParts.push(`width:${input.widthPx}px`);
|
||||
const widthEm = input.widthPx / 16;
|
||||
imgStyleParts.push(`width:${widthEm}em`);
|
||||
}
|
||||
|
||||
const radiusToken: ImageBorderRadiusToken = input.borderRadius ?? "md";
|
||||
|
||||
@@ -134,7 +134,15 @@ export const computeListPublicTokens = (props: ListBlockProps): ListPublicTokens
|
||||
}
|
||||
}
|
||||
if (props.lineHeightCustom && props.lineHeightCustom.trim() !== "") {
|
||||
listStyle.lineHeight = props.lineHeightCustom;
|
||||
const lineHeightValue = props.lineHeightCustom.trim();
|
||||
if (lineHeightValue.endsWith("px")) {
|
||||
const lineHeightEm = convertPxStringToEm(lineHeightValue);
|
||||
if (lineHeightEm) {
|
||||
listStyle.lineHeight = lineHeightEm;
|
||||
}
|
||||
} else {
|
||||
listStyle.lineHeight = lineHeightValue;
|
||||
}
|
||||
}
|
||||
if (props.textColorCustom && props.textColorCustom.trim() !== "") {
|
||||
listStyle.color = props.textColorCustom;
|
||||
|
||||
@@ -276,6 +276,17 @@ export interface TextPublicTokens {
|
||||
styleOverrides: CSSProperties;
|
||||
}
|
||||
|
||||
const pxToEm = (px: number, base = 16) => `${px / base}em`;
|
||||
|
||||
const convertPxStringToEm = (value?: string | null) => {
|
||||
if (!value) return null;
|
||||
const match = value.trim().match(/-?\d+(?:\.\d+)?/);
|
||||
if (!match) return null;
|
||||
const px = parseFloat(match[0]);
|
||||
if (!Number.isFinite(px)) return null;
|
||||
return pxToEm(px);
|
||||
};
|
||||
|
||||
export function computeTextPublicTokens(props: TextBlockProps): TextPublicTokens {
|
||||
const alignClass =
|
||||
props.align === "center"
|
||||
@@ -308,7 +319,15 @@ export function computeTextPublicTokens(props: TextBlockProps): TextPublicTokens
|
||||
props.fontSizeCustom &&
|
||||
props.fontSizeCustom.trim() !== ""
|
||||
) {
|
||||
styleOverrides.fontSize = props.fontSizeCustom;
|
||||
const fontSizeValue = props.fontSizeCustom.trim();
|
||||
if (fontSizeValue.endsWith("px")) {
|
||||
const fontSizeEm = convertPxStringToEm(fontSizeValue);
|
||||
if (fontSizeEm) {
|
||||
styleOverrides.fontSize = fontSizeEm;
|
||||
}
|
||||
} else {
|
||||
styleOverrides.fontSize = fontSizeValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (props.colorCustom && props.colorCustom.trim() !== "") {
|
||||
@@ -319,6 +338,30 @@ export function computeTextPublicTokens(props: TextBlockProps): TextPublicTokens
|
||||
styleOverrides.backgroundColor = props.backgroundColorCustom.trim();
|
||||
}
|
||||
|
||||
if (props.lineHeightCustom && props.lineHeightCustom.trim() !== "") {
|
||||
const lineHeightValue = props.lineHeightCustom.trim();
|
||||
if (lineHeightValue.endsWith("px")) {
|
||||
const lineHeightEm = convertPxStringToEm(lineHeightValue);
|
||||
if (lineHeightEm) {
|
||||
styleOverrides.lineHeight = lineHeightEm;
|
||||
}
|
||||
} else {
|
||||
styleOverrides.lineHeight = lineHeightValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (props.letterSpacingCustom && props.letterSpacingCustom.trim() !== "") {
|
||||
const letterSpacingValue = props.letterSpacingCustom.trim();
|
||||
if (letterSpacingValue.endsWith("px")) {
|
||||
const letterSpacingEm = convertPxStringToEm(letterSpacingValue);
|
||||
if (letterSpacingEm) {
|
||||
styleOverrides.letterSpacing = letterSpacingEm;
|
||||
}
|
||||
} else {
|
||||
styleOverrides.letterSpacing = letterSpacingValue;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
alignClass,
|
||||
sizeClass,
|
||||
|
||||
@@ -101,8 +101,9 @@ export function computeVideoExportTokens(
|
||||
|
||||
const widthMode = props.widthMode ?? "auto";
|
||||
if (widthMode === "fixed" && typeof props.widthPx === "number" && props.widthPx > 0) {
|
||||
wrapperStyleParts.push(`width:${props.widthPx}px`);
|
||||
videoStyleParts.push(`width:${props.widthPx}px`);
|
||||
const widthEm = pxToEm(props.widthPx);
|
||||
wrapperStyleParts.push(`width:${widthEm}`);
|
||||
videoStyleParts.push(`width:${widthEm}`);
|
||||
} else if (widthMode === "full") {
|
||||
wrapperStyleParts.push("width:100%");
|
||||
videoStyleParts.push("width:100%");
|
||||
@@ -113,11 +114,11 @@ export function computeVideoExportTokens(
|
||||
}
|
||||
|
||||
if (typeof props.cardPaddingPx === "number" && props.cardPaddingPx >= 0) {
|
||||
wrapperStyleParts.push(`padding:${props.cardPaddingPx}px`);
|
||||
wrapperStyleParts.push(`padding:${pxToEm(props.cardPaddingPx)}`);
|
||||
}
|
||||
|
||||
if (typeof props.borderRadiusPx === "number" && props.borderRadiusPx >= 0) {
|
||||
wrapperStyleParts.push(`border-radius:${props.borderRadiusPx}px`);
|
||||
wrapperStyleParts.push(`border-radius:${pxToEm(props.borderRadiusPx)}`);
|
||||
}
|
||||
|
||||
const align =
|
||||
|
||||
Reference in New Issue
Block a user