From e14db1629055efa16a9bdc46a1938601bae5adb3 Mon Sep 17 00:00:00 2001 From: Dmitri Date: Sat, 27 Jun 2026 13:50:58 +0200 Subject: [PATCH] Added ability to set external URL for navigation buttons --- backend/src/services/tour_pages.js | 4 + .../Constructor/ElementEditorPanel.tsx | 33 +- frontend/src/components/Constructor/types.ts | 1 - .../NavigationSettingsSectionCompact.tsx | 439 ++++++++++-------- .../src/components/ElementSettings/types.ts | 2 + .../ElementSettings/useElementSettingsForm.ts | 11 + .../src/components/RuntimePresentation.tsx | 11 + frontend/src/context/ConstructorContext.tsx | 5 - frontend/src/lib/elementDefaults.ts | 7 + frontend/src/lib/extractPageLinks.ts | 4 +- frontend/src/pages/constructor.tsx | 65 +-- frontend/src/types/constructor.ts | 10 +- frontend/src/types/presentation.ts | 2 + 13 files changed, 358 insertions(+), 236 deletions(-) diff --git a/backend/src/services/tour_pages.js b/backend/src/services/tour_pages.js index 1512641..d3a9652 100644 --- a/backend/src/services/tour_pages.js +++ b/backend/src/services/tour_pages.js @@ -483,6 +483,10 @@ class TourPagesService extends BaseService { * @private */ static isForwardElementWithTarget(element) { + if (element.navigationTargetMode === 'external_url') { + return false; + } + const isForward = element.type === 'navigation_next' || (element.type?.startsWith?.('navigation') && diff --git a/frontend/src/components/Constructor/ElementEditorPanel.tsx b/frontend/src/components/Constructor/ElementEditorPanel.tsx index f2113a8..b797c5b 100644 --- a/frontend/src/components/Constructor/ElementEditorPanel.tsx +++ b/frontend/src/components/Constructor/ElementEditorPanel.tsx @@ -175,7 +175,6 @@ export function ElementEditorPanel({ activePageId, allowedNavigationTypes, normalizeNavigationType, - onPreviewTransition, } = useConstructorNavigation(); const { activeTab, setActiveTab } = useConstructorEditorTab(); @@ -336,7 +335,11 @@ export function ElementEditorPanel({ } navDisabled={selectedElement.navDisabled || false} iconUrl={selectedElement.iconUrl || ''} + navigationTargetMode={ + selectedElement.navigationTargetMode || 'target_page' + } targetPageSlug={selectedElement.targetPageSlug || ''} + externalUrl={selectedElement.externalUrl || ''} transitionVideoUrl={ selectedElement.transitionVideoUrl || '' } @@ -363,10 +366,25 @@ export function ElementEditorPanel({ } onChange={(prop, value) => { if (prop === 'type') { - const nextType = value as NavigationElementType; - updateSelectedElement( - normalizeNavigationType(selectedElement, nextType), - ); + if (typeof value === 'object') { + const nextType = (value.type || + selectedElement.type) as NavigationElementType; + updateSelectedElement({ + ...normalizeNavigationType( + selectedElement, + nextType, + ), + ...value, + }); + } else { + const nextType = value as NavigationElementType; + updateSelectedElement( + normalizeNavigationType( + selectedElement, + nextType, + ), + ); + } } else if (prop === 'transitionVideoUrl') { const nextVideoUrl = value as string; const resolvedDuration = getDuration(nextVideoUrl); @@ -380,13 +398,16 @@ export function ElementEditorPanel({ targetPageSlug: value as string, targetPageId: '', }); + } else if (prop === 'navigationTargetMode') { + if (typeof value === 'object') { + updateSelectedElement(value); + } } else { updateSelectedElement({ [prop]: value, }); } }} - onPreviewTransition={onPreviewTransition} /> )} diff --git a/frontend/src/components/Constructor/types.ts b/frontend/src/components/Constructor/types.ts index 3d36ba4..b9345eb 100644 --- a/frontend/src/components/Constructor/types.ts +++ b/frontend/src/components/Constructor/types.ts @@ -203,7 +203,6 @@ export interface ElementEditorPanelProps { onUpdateTransitionVideoUrl: (url: string) => void; onUpdateTransitionSupportsReverse: (value: boolean) => void; onCreateTransition: () => void; - onPreviewTransition: (direction: 'forward' | 'back') => void; // Gallery operations onAddGalleryCard: () => void; diff --git a/frontend/src/components/ElementSettings/NavigationSettingsSectionCompact.tsx b/frontend/src/components/ElementSettings/NavigationSettingsSectionCompact.tsx index 2cabb84..3617598 100644 --- a/frontend/src/components/ElementSettings/NavigationSettingsSectionCompact.tsx +++ b/frontend/src/components/ElementSettings/NavigationSettingsSectionCompact.tsx @@ -6,10 +6,10 @@ */ import React from 'react'; -import BaseButton from '../BaseButton'; import type { AssetOption, NavigationButtonKind, + NavigationTargetMode, CanvasElementType, } from '../../types/constructor'; import type { TransitionType, EasingFunction } from '../../types/transition'; @@ -42,7 +42,9 @@ interface NavigationSettingsSectionCompactProps { navLabelFontFamily: string; navDisabled: boolean; iconUrl: string; + navigationTargetMode: NavigationTargetMode; targetPageSlug: string; + externalUrl: string; transitionVideoUrl: string; transitionReverseMode: 'auto_reverse' | 'separate_video'; reverseVideoUrl: string; @@ -67,11 +69,14 @@ interface NavigationSettingsSectionCompactProps { | Partial<{ type: NavigationElementType; navType: NavigationButtonKind; + navigationTargetMode: NavigationTargetMode; label?: string; navLabel?: string; + targetPageSlug?: string; + targetPageId?: string; + externalUrl?: string; }>, ) => void; - onPreviewTransition?: (direction: 'forward' | 'back') => void; } const NavigationSettingsSectionCompact: React.FC< @@ -83,7 +88,9 @@ const NavigationSettingsSectionCompact: React.FC< navLabelFontFamily, navDisabled, iconUrl, + navigationTargetMode, targetPageSlug, + externalUrl, transitionVideoUrl, transitionReverseMode, reverseVideoUrl, @@ -99,10 +106,13 @@ const NavigationSettingsSectionCompact: React.FC< selectedMediaDurationNote, selectedTransitionDurationNote, onChange, - onPreviewTransition, }) => { + const currentTargetMode: NavigationTargetMode = + navigationTargetMode === 'external_url' ? 'external_url' : 'target_page'; const currentKind: NavigationButtonKind = - navType || (type === 'navigation_prev' ? 'back' : 'forward'); + currentTargetMode === 'external_url' + ? 'forward' + : navType || (type === 'navigation_prev' ? 'back' : 'forward'); return (
@@ -121,7 +131,15 @@ const NavigationSettingsSectionCompact: React.FC< const nextType = allowedNavigationTypes.includes(requestedType) ? requestedType : allowedNavigationTypes[0]; - onChange('type', nextType); + onChange('type', { + type: nextType, + navType: requestedKind, + navigationTargetMode: + requestedKind === 'back' ? 'target_page' : currentTargetMode, + targetPageSlug: requestedKind === 'back' ? '' : targetPageSlug, + targetPageId: '', + externalUrl: requestedKind === 'back' ? '' : externalUrl, + }); }} >