fixed font selection issue

This commit is contained in:
Dmitri 2026-03-31 10:26:13 +04:00
parent e92ab8143b
commit e6b4fe69c7
22 changed files with 175 additions and 50 deletions

View File

@ -366,6 +366,7 @@ export function ElementEditorPanel({
} }
navType={selectedElement.navType} navType={selectedElement.navType}
navLabel={selectedElement.navLabel || ''} navLabel={selectedElement.navLabel || ''}
navLabelFontFamily={selectedElement.navLabelFontFamily || ''}
navDisabled={selectedElement.navDisabled || false} navDisabled={selectedElement.navDisabled || false}
iconUrl={selectedElement.iconUrl || ''} iconUrl={selectedElement.iconUrl || ''}
targetPageSlug={selectedElement.targetPageSlug || ''} targetPageSlug={selectedElement.targetPageSlug || ''}
@ -506,8 +507,8 @@ export function ElementEditorPanel({
galleryTitleFontFamily={ galleryTitleFontFamily={
selectedElement.galleryTitleFontFamily || '' selectedElement.galleryTitleFontFamily || ''
} }
galleryCardFontFamily={ galleryTextFontFamily={
selectedElement.galleryCardFontFamily || '' selectedElement.galleryTextFontFamily || ''
} }
galleryCards={selectedElement.galleryCards || []} galleryCards={selectedElement.galleryCards || []}
imageAssetOptions={imageAssetOptions} imageAssetOptions={imageAssetOptions}

View File

@ -98,7 +98,7 @@ const CarouselSettingsSection: React.FC<CarouselSettingsSectionProps> = ({
> >
<option value=''>Not set</option> <option value=''>Not set</option>
{FONT_OPTIONS.map((font) => ( {FONT_OPTIONS.map((font) => (
<option key={font.key} value={font.fontFamily}> <option key={font.key} value={font.key}>
{font.label} {font.label}
</option> </option>
))} ))}

View File

@ -97,7 +97,7 @@ const CarouselSettingsSectionCompact: React.FC<
> >
<option value=''>Not set</option> <option value=''>Not set</option>
{FONT_OPTIONS.map((font) => ( {FONT_OPTIONS.map((font) => (
<option key={font.key} value={font.fontFamily}> <option key={font.key} value={font.key}>
{font.label} {font.label}
</option> </option>
))} ))}

View File

@ -94,7 +94,7 @@ const DescriptionSettingsSection: React.FC<DescriptionSettingsSectionProps> = ({
> >
<option value=''>Not set</option> <option value=''>Not set</option>
{FONT_OPTIONS.map((font) => ( {FONT_OPTIONS.map((font) => (
<option key={font.key} value={font.fontFamily}> <option key={font.key} value={font.key}>
{font.label} {font.label}
</option> </option>
))} ))}
@ -109,7 +109,7 @@ const DescriptionSettingsSection: React.FC<DescriptionSettingsSectionProps> = ({
> >
<option value=''>Not set</option> <option value=''>Not set</option>
{FONT_OPTIONS.map((font) => ( {FONT_OPTIONS.map((font) => (
<option key={font.key} value={font.fontFamily}> <option key={font.key} value={font.key}>
{font.label} {font.label}
</option> </option>
))} ))}

View File

@ -129,7 +129,7 @@ const DescriptionSettingsSectionCompact: React.FC<
> >
<option value=''>Not set</option> <option value=''>Not set</option>
{FONT_OPTIONS.map((font) => ( {FONT_OPTIONS.map((font) => (
<option key={font.key} value={font.fontFamily}> <option key={font.key} value={font.key}>
{font.label} {font.label}
</option> </option>
))} ))}
@ -149,7 +149,7 @@ const DescriptionSettingsSectionCompact: React.FC<
> >
<option value=''>Not set</option> <option value=''>Not set</option>
{FONT_OPTIONS.map((font) => ( {FONT_OPTIONS.map((font) => (
<option key={font.key} value={font.fontFamily}> <option key={font.key} value={font.key}>
{font.label} {font.label}
</option> </option>
))} ))}

View File

@ -16,7 +16,7 @@ import { FONT_OPTIONS } from '../../lib/fonts';
const GallerySettingsSection: React.FC<GallerySettingsSectionProps> = ({ const GallerySettingsSection: React.FC<GallerySettingsSectionProps> = ({
galleryCards, galleryCards,
galleryTitleFontFamily, galleryTitleFontFamily,
galleryCardFontFamily, galleryTextFontFamily,
onAddCard, onAddCard,
onRemoveCard, onRemoveCard,
onUpdateCard, onUpdateCard,
@ -40,22 +40,22 @@ const GallerySettingsSection: React.FC<GallerySettingsSectionProps> = ({
> >
<option value=''>Not set</option> <option value=''>Not set</option>
{FONT_OPTIONS.map((font) => ( {FONT_OPTIONS.map((font) => (
<option key={font.key} value={font.fontFamily}> <option key={font.key} value={font.key}>
{font.label} {font.label}
</option> </option>
))} ))}
</select> </select>
</FormField> </FormField>
<FormField label='Card font family'> <FormField label='Text font family'>
<select <select
value={galleryCardFontFamily} value={galleryTextFontFamily}
onChange={(event) => onChange={(event) =>
onChange('galleryCardFontFamily', event.target.value) onChange('galleryTextFontFamily', event.target.value)
} }
> >
<option value=''>Not set</option> <option value=''>Not set</option>
{FONT_OPTIONS.map((font) => ( {FONT_OPTIONS.map((font) => (
<option key={font.key} value={font.fontFamily}> <option key={font.key} value={font.key}>
{font.label} {font.label}
</option> </option>
))} ))}

View File

@ -22,7 +22,7 @@ interface GallerySettingsSectionCompactProps {
galleryColumns: number; galleryColumns: number;
// Font settings // Font settings
galleryTitleFontFamily: string; galleryTitleFontFamily: string;
galleryCardFontFamily: string; galleryTextFontFamily: string;
// Cards // Cards
galleryCards: GalleryCard[]; galleryCards: GalleryCard[];
imageAssetOptions: AssetOption[]; imageAssetOptions: AssetOption[];
@ -32,7 +32,7 @@ interface GallerySettingsSectionCompactProps {
galleryTitle?: string; galleryTitle?: string;
galleryColumns?: number; galleryColumns?: number;
galleryTitleFontFamily?: string; galleryTitleFontFamily?: string;
galleryCardFontFamily?: string; galleryTextFontFamily?: string;
}) => void; }) => void;
onAddInfoSpan: () => void; onAddInfoSpan: () => void;
onUpdateInfoSpan: (spanId: string, text: string) => void; onUpdateInfoSpan: (spanId: string, text: string) => void;
@ -51,7 +51,7 @@ const GallerySettingsSectionCompact: React.FC<
galleryInfoSpans, galleryInfoSpans,
galleryColumns, galleryColumns,
galleryTitleFontFamily, galleryTitleFontFamily,
galleryCardFontFamily, galleryTextFontFamily,
galleryCards, galleryCards,
imageAssetOptions, imageAssetOptions,
onUpdateHeader, onUpdateHeader,
@ -123,7 +123,7 @@ const GallerySettingsSectionCompact: React.FC<
> >
<option value=''>Not set</option> <option value=''>Not set</option>
{FONT_OPTIONS.map((font) => ( {FONT_OPTIONS.map((font) => (
<option key={font.key} value={font.fontFamily}> <option key={font.key} value={font.key}>
{font.label} {font.label}
</option> </option>
))} ))}
@ -131,17 +131,17 @@ const GallerySettingsSectionCompact: React.FC<
</div> </div>
<div> <div>
<label className='text-[10px] text-gray-600'>Card font:</label> <label className='text-[10px] text-gray-600'>Text font:</label>
<select <select
className='w-full rounded border border-gray-300 px-2 py-1 text-xs' className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={galleryCardFontFamily} value={galleryTextFontFamily}
onChange={(event) => onChange={(event) =>
onUpdateHeader({ galleryCardFontFamily: event.target.value }) onUpdateHeader({ galleryTextFontFamily: event.target.value })
} }
> >
<option value=''>Not set</option> <option value=''>Not set</option>
{FONT_OPTIONS.map((font) => ( {FONT_OPTIONS.map((font) => (
<option key={font.key} value={font.fontFamily}> <option key={font.key} value={font.key}>
{font.label} {font.label}
</option> </option>
))} ))}

View File

@ -7,10 +7,12 @@
import React from 'react'; import React from 'react';
import FormField from '../FormField'; import FormField from '../FormField';
import type { NavigationSettingsSectionProps } from './types'; import type { NavigationSettingsSectionProps } from './types';
import { FONT_OPTIONS } from '../../lib/fonts';
const NavigationSettingsSection: React.FC<NavigationSettingsSectionProps> = ({ const NavigationSettingsSection: React.FC<NavigationSettingsSectionProps> = ({
iconUrl, iconUrl,
navLabel, navLabel,
navLabelFontFamily,
navType, navType,
navDisabled, navDisabled,
targetPageId, targetPageId,
@ -64,6 +66,22 @@ const NavigationSettingsSection: React.FC<NavigationSettingsSectionProps> = ({
/> />
</FormField> </FormField>
<FormField label='Label font family'>
<select
value={navLabelFontFamily}
onChange={(event) =>
onChange('navLabelFontFamily', event.target.value)
}
>
<option value=''>Not set</option>
{FONT_OPTIONS.map((font) => (
<option key={font.key} value={font.key}>
{font.label}
</option>
))}
</select>
</FormField>
<FormField label='Navigation type'> <FormField label='Navigation type'>
<select <select
value={navType} value={navType}

View File

@ -13,6 +13,7 @@ import type {
CanvasElementType, CanvasElementType,
} from '../../types/constructor'; } from '../../types/constructor';
import { addFallbackAssetOption } from '../../lib/constructorHelpers'; import { addFallbackAssetOption } from '../../lib/constructorHelpers';
import { FONT_OPTIONS } from '../../lib/fonts';
type NavigationElementType = Extract< type NavigationElementType = Extract<
CanvasElementType, CanvasElementType,
@ -23,6 +24,7 @@ interface NavigationSettingsSectionCompactProps {
type: NavigationElementType; type: NavigationElementType;
navType?: NavigationButtonKind; navType?: NavigationButtonKind;
navLabel: string; navLabel: string;
navLabelFontFamily: string;
navDisabled: boolean; navDisabled: boolean;
iconUrl: string; iconUrl: string;
targetPageSlug: string; targetPageSlug: string;
@ -58,6 +60,7 @@ const NavigationSettingsSectionCompact: React.FC<
type, type,
navType, navType,
navLabel, navLabel,
navLabelFontFamily,
navDisabled, navDisabled,
iconUrl, iconUrl,
targetPageSlug, targetPageSlug,
@ -123,6 +126,24 @@ const NavigationSettingsSectionCompact: React.FC<
/> />
</div> </div>
<div>
<label className='mb-1 block text-[11px] font-semibold text-gray-600'>
Label font
</label>
<select
className='w-full rounded border border-gray-300 px-2 py-1 text-xs'
value={navLabelFontFamily}
onChange={(event) => onChange('navLabelFontFamily', event.target.value)}
>
<option value=''>Not set</option>
{FONT_OPTIONS.map((font) => (
<option key={font.key} value={font.key}>
{font.label}
</option>
))}
</select>
</div>
<div> <div>
<label className='flex items-center gap-2 text-[11px] font-semibold text-gray-600'> <label className='flex items-center gap-2 text-[11px] font-semibold text-gray-600'>
<input <input

View File

@ -71,7 +71,7 @@ const TooltipSettingsSection: React.FC<TooltipSettingsSectionProps> = ({
> >
<option value=''>Not set</option> <option value=''>Not set</option>
{FONT_OPTIONS.map((font) => ( {FONT_OPTIONS.map((font) => (
<option key={font.key} value={font.fontFamily}> <option key={font.key} value={font.key}>
{font.label} {font.label}
</option> </option>
))} ))}
@ -86,7 +86,7 @@ const TooltipSettingsSection: React.FC<TooltipSettingsSectionProps> = ({
> >
<option value=''>Not set</option> <option value=''>Not set</option>
{FONT_OPTIONS.map((font) => ( {FONT_OPTIONS.map((font) => (
<option key={font.key} value={font.fontFamily}> <option key={font.key} value={font.key}>
{font.label} {font.label}
</option> </option>
))} ))}

View File

@ -91,7 +91,7 @@ const TooltipSettingsSectionCompact: React.FC<
> >
<option value=''>Not set</option> <option value=''>Not set</option>
{FONT_OPTIONS.map((font) => ( {FONT_OPTIONS.map((font) => (
<option key={font.key} value={font.fontFamily}> <option key={font.key} value={font.key}>
{font.label} {font.label}
</option> </option>
))} ))}
@ -111,7 +111,7 @@ const TooltipSettingsSectionCompact: React.FC<
> >
<option value=''>Not set</option> <option value=''>Not set</option>
{FONT_OPTIONS.map((font) => ( {FONT_OPTIONS.map((font) => (
<option key={font.key} value={font.fontFamily}> <option key={font.key} value={font.key}>
{font.label} {font.label}
</option> </option>
))} ))}

View File

@ -83,6 +83,7 @@ export interface CommonSettingsSectionProps {
export interface NavigationSettingsSectionProps { export interface NavigationSettingsSectionProps {
iconUrl: string; iconUrl: string;
navLabel: string; navLabel: string;
navLabelFontFamily: string;
navType: 'forward' | 'back'; navType: 'forward' | 'back';
navDisabled: boolean; navDisabled: boolean;
targetPageId: string; targetPageId: string;
@ -152,7 +153,7 @@ export interface MediaSettingsSectionProps {
export interface GallerySettingsSectionProps { export interface GallerySettingsSectionProps {
galleryCards: GalleryCard[]; galleryCards: GalleryCard[];
galleryTitleFontFamily: string; galleryTitleFontFamily: string;
galleryCardFontFamily: string; galleryTextFontFamily: string;
onAddCard: () => void; onAddCard: () => void;
onRemoveCard: (cardId: string) => void; onRemoveCard: (cardId: string) => void;
onUpdateCard: ( onUpdateCard: (
@ -177,7 +178,7 @@ export interface GallerySettingsSectionCompactProps {
galleryColumns: number; galleryColumns: number;
// Font settings // Font settings
galleryTitleFontFamily: string; galleryTitleFontFamily: string;
galleryCardFontFamily: string; galleryTextFontFamily: string;
// Cards // Cards
galleryCards: GalleryCard[]; galleryCards: GalleryCard[];
imageAssetOptions: AssetOption[]; imageAssetOptions: AssetOption[];
@ -187,7 +188,7 @@ export interface GallerySettingsSectionCompactProps {
galleryTitle?: string; galleryTitle?: string;
galleryColumns?: number; galleryColumns?: number;
galleryTitleFontFamily?: string; galleryTitleFontFamily?: string;
galleryCardFontFamily?: string; galleryTextFontFamily?: string;
}) => void; }) => void;
// Info span handlers // Info span handlers
onAddInfoSpan: () => void; onAddInfoSpan: () => void;

View File

@ -91,6 +91,7 @@ interface FormState {
// Navigation settings // Navigation settings
iconUrl: string; iconUrl: string;
navLabel: string; navLabel: string;
navLabelFontFamily: string;
navType: 'forward' | 'back'; navType: 'forward' | 'back';
navDisabled: boolean; navDisabled: boolean;
targetPageId: string; targetPageId: string;
@ -129,7 +130,7 @@ interface FormState {
// Gallery settings // Gallery settings
galleryTitleFontFamily: string; galleryTitleFontFamily: string;
galleryCardFontFamily: string; galleryTextFontFamily: string;
// Complex arrays // Complex arrays
galleryCards: GalleryCard[]; galleryCards: GalleryCard[];
@ -186,6 +187,7 @@ const initialState: FormState = {
activeBackgroundColor: '', activeBackgroundColor: '',
iconUrl: '', iconUrl: '',
navLabel: '', navLabel: '',
navLabelFontFamily: '',
navType: 'forward', navType: 'forward',
navDisabled: false, navDisabled: false,
targetPageId: '', targetPageId: '',
@ -214,7 +216,7 @@ const initialState: FormState = {
carouselNextIconUrl: '', carouselNextIconUrl: '',
carouselCaptionFontFamily: '', carouselCaptionFontFamily: '',
galleryTitleFontFamily: '', galleryTitleFontFamily: '',
galleryCardFontFamily: '', galleryTextFontFamily: '',
galleryCards: [], galleryCards: [],
carouselSlides: [], carouselSlides: [],
}; };
@ -295,6 +297,7 @@ export function useElementSettingsForm(options: UseElementSettingsFormOptions) {
: String(settings.appearDurationSec), : String(settings.appearDurationSec),
iconUrl: String(settings.iconUrl || ''), iconUrl: String(settings.iconUrl || ''),
navLabel: String(settings.navLabel || ''), navLabel: String(settings.navLabel || ''),
navLabelFontFamily: String(settings.navLabelFontFamily || ''),
navType: settings.navType === 'back' ? 'back' : 'forward', navType: settings.navType === 'back' ? 'back' : 'forward',
navDisabled: Boolean(settings.navDisabled), navDisabled: Boolean(settings.navDisabled),
targetPageId: String(settings.targetPageId || ''), targetPageId: String(settings.targetPageId || ''),
@ -334,7 +337,7 @@ export function useElementSettingsForm(options: UseElementSettingsFormOptions) {
carouselNextIconUrl: String(settings.carouselNextIconUrl || ''), carouselNextIconUrl: String(settings.carouselNextIconUrl || ''),
carouselCaptionFontFamily: String(settings.carouselCaptionFontFamily || ''), carouselCaptionFontFamily: String(settings.carouselCaptionFontFamily || ''),
galleryTitleFontFamily: String(settings.galleryTitleFontFamily || ''), galleryTitleFontFamily: String(settings.galleryTitleFontFamily || ''),
galleryCardFontFamily: String(settings.galleryCardFontFamily || ''), galleryTextFontFamily: String(settings.galleryTextFontFamily || ''),
galleryCards: Array.isArray(settings.galleryCards) galleryCards: Array.isArray(settings.galleryCards)
? settings.galleryCards.map( ? settings.galleryCards.map(
(card: Record<string, unknown>, index: number) => ({ (card: Record<string, unknown>, index: number) => ({
@ -638,6 +641,7 @@ export function useElementSettingsForm(options: UseElementSettingsFormOptions) {
if (isNavigationType) { if (isNavigationType) {
settings.iconUrl = state.iconUrl.trim(); settings.iconUrl = state.iconUrl.trim();
settings.navLabel = state.navLabel.trim(); settings.navLabel = state.navLabel.trim();
settings.navLabelFontFamily = state.navLabelFontFamily.trim();
settings.navType = state.navType; settings.navType = state.navType;
settings.navDisabled = state.navDisabled; settings.navDisabled = state.navDisabled;
settings.targetPageId = state.targetPageId.trim(); settings.targetPageId = state.targetPageId.trim();
@ -686,7 +690,7 @@ export function useElementSettingsForm(options: UseElementSettingsFormOptions) {
description: card.description, description: card.description,
})); }));
settings.galleryTitleFontFamily = state.galleryTitleFontFamily.trim(); settings.galleryTitleFontFamily = state.galleryTitleFontFamily.trim();
settings.galleryCardFontFamily = state.galleryCardFontFamily.trim(); settings.galleryTextFontFamily = state.galleryTextFontFamily.trim();
} }
// Carousel type settings // Carousel type settings

View File

@ -5,10 +5,11 @@
* Renders with unified wrapper styling + content. * Renders with unified wrapper styling + content.
*/ */
import React from 'react'; import React, { useMemo } from 'react';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
import type { CanvasElement, CarouselSlide } from '../../../types/constructor'; import type { CanvasElement, CarouselSlide } from '../../../types/constructor';
import { resolveAssetPlaybackUrl } from '../../../lib/assetUrl'; import { resolveAssetPlaybackUrl } from '../../../lib/assetUrl';
import { getFontByKey, getFontStyle } from '../../../lib/fonts';
interface CarouselElementProps { interface CarouselElementProps {
element: CanvasElement; element: CanvasElement;
@ -27,6 +28,14 @@ const CarouselElement: React.FC<CarouselElementProps> = ({
const slides: CarouselSlide[] = element.carouselSlides || []; const slides: CarouselSlide[] = element.carouselSlides || [];
const firstSlide = slides[0]; const firstSlide = slides[0];
// Resolve font key to full CSS style (including fontStretch for condensed variants)
const captionFontStyle = useMemo(() => {
const fontKey = element.carouselCaptionFontFamily;
if (!fontKey) return {};
const font = getFontByKey(fontKey);
return font ? getFontStyle(font) : { fontFamily: fontKey };
}, [element.carouselCaptionFontFamily]);
return ( return (
<div className={className} style={style}> <div className={className} style={style}>
<div className='relative w-full h-full min-w-[120px] min-h-[80px]'> <div className='relative w-full h-full min-w-[120px] min-h-[80px]'>

View File

@ -5,10 +5,11 @@
* Renders with unified wrapper styling + content. * Renders with unified wrapper styling + content.
*/ */
import React from 'react'; import React, { useMemo } from 'react';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
import type { CanvasElement } from '../../../types/constructor'; import type { CanvasElement } from '../../../types/constructor';
import { resolveAssetPlaybackUrl } from '../../../lib/assetUrl'; import { resolveAssetPlaybackUrl } from '../../../lib/assetUrl';
import { getFontByKey, getFontStyle } from '../../../lib/fonts';
interface DescriptionElementProps { interface DescriptionElementProps {
element: CanvasElement; element: CanvasElement;
@ -25,6 +26,21 @@ const DescriptionElement: React.FC<DescriptionElementProps> = ({
}) => { }) => {
const resolve = resolveUrl ?? resolveAssetPlaybackUrl; const resolve = resolveUrl ?? resolveAssetPlaybackUrl;
// Resolve font keys to full CSS styles (including fontStretch for condensed variants)
const titleFontStyle = useMemo(() => {
const fontKey = element.descriptionTitleFontFamily;
if (!fontKey) return {};
const font = getFontByKey(fontKey);
return font ? getFontStyle(font) : { fontFamily: fontKey };
}, [element.descriptionTitleFontFamily]);
const textFontStyle = useMemo(() => {
const fontKey = element.descriptionTextFontFamily;
if (!fontKey) return {};
const font = getFontByKey(fontKey);
return font ? getFontStyle(font) : { fontFamily: fontKey };
}, [element.descriptionTextFontFamily]);
// With icon: render image // With icon: render image
if (element.iconUrl) { if (element.iconUrl) {
const imgStyle: CSSProperties = { const imgStyle: CSSProperties = {
@ -56,8 +72,8 @@ const DescriptionElement: React.FC<DescriptionElementProps> = ({
className='font-bold' className='font-bold'
style={{ style={{
fontSize: element.descriptionTitleFontSize || '24px', fontSize: element.descriptionTitleFontSize || '24px',
fontFamily: element.descriptionTitleFontFamily || 'inherit',
color: element.descriptionTitleColor || '#ffffff', color: element.descriptionTitleColor || '#ffffff',
...titleFontStyle,
}} }}
> >
{element.descriptionTitle || ''} {element.descriptionTitle || ''}
@ -66,8 +82,8 @@ const DescriptionElement: React.FC<DescriptionElementProps> = ({
<p <p
style={{ style={{
fontSize: element.descriptionTextFontSize || '16px', fontSize: element.descriptionTextFontSize || '16px',
fontFamily: element.descriptionTextFontFamily || 'inherit',
color: element.descriptionTextColor || '#ffffff', color: element.descriptionTextColor || '#ffffff',
...textFontStyle,
}} }}
> >
{element.descriptionText} {element.descriptionText}

View File

@ -5,7 +5,7 @@
* Renders with unified wrapper styling + content. * Renders with unified wrapper styling + content.
*/ */
import React from 'react'; import React, { useMemo } from 'react';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
import type { import type {
CanvasElement, CanvasElement,
@ -13,6 +13,7 @@ import type {
GalleryInfoSpan, GalleryInfoSpan,
} from '../../../types/constructor'; } from '../../../types/constructor';
import { resolveAssetPlaybackUrl } from '../../../lib/assetUrl'; import { resolveAssetPlaybackUrl } from '../../../lib/assetUrl';
import { getFontByKey, getFontStyle } from '../../../lib/fonts';
interface GalleryElementProps { interface GalleryElementProps {
element: CanvasElement; element: CanvasElement;
@ -36,6 +37,21 @@ const GalleryElement: React.FC<GalleryElementProps> = ({
const title = element.galleryTitle; const title = element.galleryTitle;
const columns = element.galleryColumns || 3; const columns = element.galleryColumns || 3;
// Resolve font keys to full CSS styles (including fontStretch for condensed variants)
const titleFontStyle = useMemo(() => {
const fontKey = element.galleryTitleFontFamily;
if (!fontKey) return {};
const font = getFontByKey(fontKey);
return font ? getFontStyle(font) : { fontFamily: fontKey };
}, [element.galleryTitleFontFamily]);
const textFontStyle = useMemo(() => {
const fontKey = element.galleryTextFontFamily;
if (!fontKey) return {};
const font = getFontByKey(fontKey);
return font ? getFontStyle(font) : { fontFamily: fontKey };
}, [element.galleryTextFontFamily]);
return ( return (
<div className={className} style={style}> <div className={className} style={style}>
<div className='flex flex-col gap-2 p-3 bg-black/60 rounded-xl min-w-[200px] backdrop-blur-sm'> <div className='flex flex-col gap-2 p-3 bg-black/60 rounded-xl min-w-[200px] backdrop-blur-sm'>
@ -52,7 +68,10 @@ const GalleryElement: React.FC<GalleryElementProps> = ({
{/* Title */} {/* Title */}
{title && ( {title && (
<div className='bg-amber-50 text-slate-800 text-center py-2 px-3 rounded-lg font-semibold text-sm'> <div
className='bg-amber-50 text-slate-800 text-center py-2 px-3 rounded-lg font-semibold text-sm'
style={titleFontStyle}
>
{title} {title}
</div> </div>
)} )}
@ -67,6 +86,7 @@ const GalleryElement: React.FC<GalleryElementProps> = ({
<div <div
key={span.id} key={span.id}
className='bg-slate-700 text-amber-50 text-center py-2 px-2 rounded-lg text-xs font-medium' className='bg-slate-700 text-amber-50 text-center py-2 px-2 rounded-lg text-xs font-medium'
style={textFontStyle}
> >
{span.text} {span.text}
</div> </div>
@ -106,7 +126,10 @@ const GalleryElement: React.FC<GalleryElementProps> = ({
)} )}
{card.title && ( {card.title && (
<div className='absolute inset-0 flex items-center justify-center'> <div className='absolute inset-0 flex items-center justify-center'>
<span className='text-white text-xs font-bold drop-shadow-lg'> <span
className='text-white text-xs font-bold drop-shadow-lg'
style={textFontStyle}
>
{card.title} {card.title}
</span> </span>
</div> </div>

View File

@ -5,10 +5,11 @@
* Renders with unified wrapper styling + content. * Renders with unified wrapper styling + content.
*/ */
import React from 'react'; import React, { useMemo } from 'react';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
import type { CanvasElement } from '../../../types/constructor'; import type { CanvasElement } from '../../../types/constructor';
import { resolveAssetPlaybackUrl } from '../../../lib/assetUrl'; import { resolveAssetPlaybackUrl } from '../../../lib/assetUrl';
import { getFontByKey, getFontStyle } from '../../../lib/fonts';
interface NavigationElementProps { interface NavigationElementProps {
element: CanvasElement; element: CanvasElement;
@ -25,6 +26,14 @@ const NavigationElement: React.FC<NavigationElementProps> = ({
}) => { }) => {
const resolve = resolveUrl ?? resolveAssetPlaybackUrl; const resolve = resolveUrl ?? resolveAssetPlaybackUrl;
// Resolve font key to full CSS style (including fontStretch for condensed variants)
const labelFontStyle = useMemo(() => {
const fontKey = element.navLabelFontFamily;
if (!fontKey) return {};
const font = getFontByKey(fontKey);
return font ? getFontStyle(font) : { fontFamily: fontKey };
}, [element.navLabelFontFamily]);
// With icon: render image // With icon: render image
if (element.iconUrl) { if (element.iconUrl) {
const imgStyle: CSSProperties = { const imgStyle: CSSProperties = {
@ -49,7 +58,7 @@ const NavigationElement: React.FC<NavigationElementProps> = ({
// Without icon: render text label // Without icon: render text label
return ( return (
<div className={className} style={style}> <div className={className} style={style}>
<span className='px-4 py-2 text-sm'> <span className='px-4 py-2 text-sm' style={labelFontStyle}>
{element.navLabel || {element.navLabel ||
(element.type === 'navigation_next' ? 'Next' : 'Back')} (element.type === 'navigation_next' ? 'Next' : 'Back')}
</span> </span>

View File

@ -5,10 +5,11 @@
* Renders with unified wrapper styling + content. * Renders with unified wrapper styling + content.
*/ */
import React from 'react'; import React, { useMemo } from 'react';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
import type { CanvasElement } from '../../../types/constructor'; import type { CanvasElement } from '../../../types/constructor';
import { resolveAssetPlaybackUrl } from '../../../lib/assetUrl'; import { resolveAssetPlaybackUrl } from '../../../lib/assetUrl';
import { getFontByKey, getFontStyle } from '../../../lib/fonts';
interface TooltipElementProps { interface TooltipElementProps {
element: CanvasElement; element: CanvasElement;
@ -25,6 +26,21 @@ const TooltipElement: React.FC<TooltipElementProps> = ({
}) => { }) => {
const resolve = resolveUrl ?? resolveAssetPlaybackUrl; const resolve = resolveUrl ?? resolveAssetPlaybackUrl;
// Resolve font keys to full CSS styles (including fontStretch for condensed variants)
const titleFontStyle = useMemo(() => {
const fontKey = element.tooltipTitleFontFamily;
if (!fontKey) return {};
const font = getFontByKey(fontKey);
return font ? getFontStyle(font) : { fontFamily: fontKey };
}, [element.tooltipTitleFontFamily]);
const textFontStyle = useMemo(() => {
const fontKey = element.tooltipTextFontFamily;
if (!fontKey) return {};
const font = getFontByKey(fontKey);
return font ? getFontStyle(font) : { fontFamily: fontKey };
}, [element.tooltipTextFontFamily]);
// With icon: render image // With icon: render image
if (element.iconUrl) { if (element.iconUrl) {
const imgStyle: CSSProperties = { const imgStyle: CSSProperties = {
@ -50,8 +66,12 @@ const TooltipElement: React.FC<TooltipElementProps> = ({
return ( return (
<div className={className} style={style}> <div className={className} style={style}>
<div className='p-3 max-w-[200px]'> <div className='p-3 max-w-[200px]'>
<p className='font-bold text-sm'>{element.tooltipTitle}</p> <p className='font-bold text-sm' style={titleFontStyle}>
<p className='text-xs opacity-70'>{element.tooltipText}</p> {element.tooltipTitle}
</p>
<p className='text-xs opacity-70' style={textFontStyle}>
{element.tooltipText}
</p>
</div> </div>
</div> </div>
); );

View File

@ -538,8 +538,8 @@ export const buildElementSettings = (
); );
addIfNotEmpty( addIfNotEmpty(
settings, settings,
'galleryCardFontFamily', 'galleryTextFontFamily',
element.galleryCardFontFamily, element.galleryTextFontFamily,
); );
} }

View File

@ -297,6 +297,7 @@ const ElementTypeDefaultDetailsPage = () => {
<NavigationSettingsSection <NavigationSettingsSection
iconUrl={form.state.iconUrl} iconUrl={form.state.iconUrl}
navLabel={form.state.navLabel} navLabel={form.state.navLabel}
navLabelFontFamily={form.state.navLabelFontFamily}
navType={form.state.navType} navType={form.state.navType}
navDisabled={form.state.navDisabled} navDisabled={form.state.navDisabled}
targetPageId={form.state.targetPageId} targetPageId={form.state.targetPageId}
@ -352,7 +353,7 @@ const ElementTypeDefaultDetailsPage = () => {
<GallerySettingsSection <GallerySettingsSection
galleryCards={form.state.galleryCards} galleryCards={form.state.galleryCards}
galleryTitleFontFamily={form.state.galleryTitleFontFamily} galleryTitleFontFamily={form.state.galleryTitleFontFamily}
galleryCardFontFamily={form.state.galleryCardFontFamily} galleryTextFontFamily={form.state.galleryTextFontFamily}
onAddCard={form.addGalleryCard} onAddCard={form.addGalleryCard}
onRemoveCard={form.removeGalleryCard} onRemoveCard={form.removeGalleryCard}
onUpdateCard={form.updateGalleryCard} onUpdateCard={form.updateGalleryCard}

View File

@ -483,6 +483,7 @@ const ProjectElementDefaultDetailsPage = () => {
<NavigationSettingsSection <NavigationSettingsSection
iconUrl={form.state.iconUrl} iconUrl={form.state.iconUrl}
navLabel={form.state.navLabel} navLabel={form.state.navLabel}
navLabelFontFamily={form.state.navLabelFontFamily}
navType={form.state.navType} navType={form.state.navType}
navDisabled={form.state.navDisabled} navDisabled={form.state.navDisabled}
targetPageId={form.state.targetPageId} targetPageId={form.state.targetPageId}
@ -537,7 +538,7 @@ const ProjectElementDefaultDetailsPage = () => {
<GallerySettingsSection <GallerySettingsSection
galleryCards={form.state.galleryCards} galleryCards={form.state.galleryCards}
galleryTitleFontFamily={form.state.galleryTitleFontFamily} galleryTitleFontFamily={form.state.galleryTitleFontFamily}
galleryCardFontFamily={form.state.galleryCardFontFamily} galleryTextFontFamily={form.state.galleryTextFontFamily}
onAddCard={form.addGalleryCard} onAddCard={form.addGalleryCard}
onRemoveCard={form.removeGalleryCard} onRemoveCard={form.removeGalleryCard}
onUpdateCard={form.updateGalleryCard} onUpdateCard={form.updateGalleryCard}

View File

@ -98,7 +98,7 @@ export interface CanvasElement extends BaseCanvasElement {
galleryInfoSpans?: GalleryInfoSpan[]; galleryInfoSpans?: GalleryInfoSpan[];
galleryColumns?: number; galleryColumns?: number;
galleryTitleFontFamily?: string; galleryTitleFontFamily?: string;
galleryCardFontFamily?: string; galleryTextFontFamily?: string;
carouselSlides?: CarouselSlide[]; carouselSlides?: CarouselSlide[];
carouselCaptionFontFamily?: string; carouselCaptionFontFamily?: string;
carouselPrevIconUrl?: string; carouselPrevIconUrl?: string;
@ -117,6 +117,7 @@ export interface CanvasElement extends BaseCanvasElement {
descriptionTextColor?: string; descriptionTextColor?: string;
descriptionBackgroundColor?: string; descriptionBackgroundColor?: string;
navLabel?: string; navLabel?: string;
navLabelFontFamily?: string;
navType?: NavigationButtonKind; navType?: NavigationButtonKind;
navDisabled?: boolean; navDisabled?: boolean;
/** @deprecated Use targetPageSlug instead - IDs change when copied between environments */ /** @deprecated Use targetPageSlug instead - IDs change when copied between environments */