Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions src/components/Learn/tours/navigatingEditor.tour.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@
"stepInteraction": true,
"interaction": "undock-window",
"targetWindowId": "context-panel",
"targetWindowName": "Task Properties",
"fallbackContent": "Windows are flexible. Try grabbing the Task Properties header and dragging it around the canvas."
"targetWindowName": "Task Properties"
},
{
"selector": "[data-window-id=\"context-panel\"]",
Expand All @@ -91,8 +90,7 @@
"stepInteraction": true,
"interaction": "redock-window",
"targetWindowId": "context-panel",
"targetWindowName": "Task Properties",
"fallbackContent": "Windows can be docked in either sidebar. Create the perfect layout that suits you!"
"targetWindowName": "Task Properties"
},
{
"selector": "[data-tracking-id=\"v2.pipeline_editor.windows_menu\"]",
Expand Down
1 change: 0 additions & 1 deletion src/components/Learn/tours/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export type TourStep = StepType & {
resetLibrarySearch?: boolean;
ensureWindowRestored?: string;
requiresTaskSelected?: string;
fallbackContent?: string;
};

export interface TourDefinition {
Expand Down
1 change: 0 additions & 1 deletion src/components/shared/SubgraphBreadcrumbsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ export const SubgraphBreadcrumbsView = ({
onClick={() => onNavigate(index)}
className="h-6 px-2"
data-tour-crumb={isRoot ? "root" : "ancestor"}
data-tour-crumb-index={index}
{...(getCrumbTracking?.(index) ?? {})}
>
{isRoot ? (
Expand Down
21 changes: 6 additions & 15 deletions src/providers/TourProvider/TourNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,6 @@ import { tourActionLabel, type TourActionLabelInput } from "./tourActionLabels";
import { renderInline } from "./TourContent";
import { useTourProgress } from "./TourProgressContext";

const tourVisitedMax = { value: 0 };
function recordTourVisited(step: number): void {
if (step > tourVisitedMax.value) tourVisitedMax.value = step;
}
function getTourVisitedMax(): number {
return tourVisitedMax.value;
}
export function resetTourVisited(): void {
tourVisitedMax.value = 0;
}

type NextButtonProps = Parameters<NonNullable<ProviderProps["nextButton"]>>[0];

type NavButtonProps = {
Expand Down Expand Up @@ -86,7 +75,7 @@ type GatedNextButtonProps = Omit<NextButtonProps, "Button"> & {

function GatedNextButton(props: GatedNextButtonProps) {
const { Button, currentStep, stepsLength, setCurrentStep, steps } = props;
const { isStepComplete } = useTourProgress();
const { isStepComplete, maxVisitedStep } = useTourProgress();

const hiddenPlaceholder = (
<span aria-hidden className="invisible pointer-events-none">
Expand All @@ -104,7 +93,7 @@ function GatedNextButton(props: GatedNextButtonProps) {
return hiddenPlaceholder;
}

const isRevisit = currentStep < getTourVisitedMax();
const isRevisit = currentStep < maxVisitedStep;
const advance = () =>
setCurrentStep((s: number) => Math.min(s + 1, stepsLength - 1));

Expand Down Expand Up @@ -189,8 +178,10 @@ export function TourNavigation(props: NavigationProps) {

const stepsLength = steps.length;

recordTourVisited(currentStep);
const visited = getTourVisitedMax();
const { recordVisited, maxVisitedStep: visited } = useTourProgress();
useEffect(() => {
recordVisited(currentStep);
}, [recordVisited, currentStep]);

const BoundNavButton = useCallback<FC<PropsWithChildren<NavButtonProps>>>(
({ children, ...rest }) => (
Expand Down
35 changes: 21 additions & 14 deletions src/providers/TourProvider/TourPopover.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useTour } from "@reactour/tour";
import { useNavigate } from "@tanstack/react-router";
import { useEffect } from "react";
import { useEffect, useSyncExternalStore } from "react";

import { Button } from "@/components/ui/button";
import { Icon } from "@/components/ui/icon";
Expand All @@ -10,7 +10,6 @@ import { APP_ROUTES } from "@/routes/router";
import { setTourActive } from "@/utils/tourActive";
import { tracking } from "@/utils/tracking";

import { resetTourVisited } from "./TourNavigation";
import { useTourProgress } from "./TourProgressContext";

// Matches the step-number badge's ≈13px outside offset plus a small margin.
Expand Down Expand Up @@ -87,9 +86,6 @@ export function computeDefaultPopoverPosition(
const isTallStrip = targetHeight > props.windowHeight * 0.5;
const margin = 16;

// Right-anchored full-height strip (e.g. right sidebar): place popover to
// its LEFT. Reactour's "left" fallback can swap to "top"/"bottom" for tall
// targets, so we return explicit coords.
if (isTallStrip && props.right >= props.windowWidth - 4) {
const popoverWidth = props.width || 380;
return [
Expand All @@ -98,12 +94,6 @@ export function computeDefaultPopoverPosition(
];
}

// Left-anchored full-height strip (e.g. left dock): place popover to its
// RIGHT. Same reason — reactour's "right" fallback drops to "top" for tall
// targets even when there's plenty of room horizontally. We test the
// target's right edge against the viewport midline rather than its left
// edge against zero, so a dock that isn't flush to the window edge still
// qualifies.
if (isTallStrip && props.right < props.windowWidth * 0.5) {
return [props.right + margin, Math.max(props.top + margin, 64)];
}
Expand All @@ -127,21 +117,39 @@ export function computeDefaultPopoverPosition(
}

let saveExploreHandler: (() => void) | null = null;
const saveExploreListeners = new Set<() => void>();

export function registerSaveExploreHandler(
handler: (() => void) | null,
): () => void {
saveExploreHandler = handler;
saveExploreListeners.forEach((listener) => listener());
return () => {
if (saveExploreHandler === handler) {
saveExploreHandler = null;
saveExploreListeners.forEach((listener) => listener());
}
};
}

function subscribeSaveExplore(listener: () => void): () => void {
saveExploreListeners.add(listener);
return () => {
saveExploreListeners.delete(listener);
};
}

function getSaveExploreHandler(): (() => void) | null {
return saveExploreHandler;
}

export function TourCompletionActions() {
const navigate = useNavigate();
const { setIsOpen } = useTour();
const saveHandler = useSyncExternalStore(
subscribeSaveExplore,
getSaveExploreHandler,
);

const onDone = () => {
setIsOpen(false);
Expand All @@ -150,7 +158,7 @@ export function TourCompletionActions() {

const onSavePipeline = () => {
setIsOpen(false);
saveExploreHandler?.();
saveHandler?.();
};

return (
Expand All @@ -164,7 +172,7 @@ export function TourCompletionActions() {
<Icon name="Check" size="sm" />
Finish Tour
</Button>
{saveExploreHandler && (
{saveHandler && (
<BlockStack align="center">
<Text size="xs" tone="subdued">
Continue exploring:
Expand Down Expand Up @@ -216,7 +224,6 @@ export function PopoverClampBridge() {

useEffect(() => {
if (isOpen) {
resetTourVisited();
resetTourProgress();
}
}, [isOpen, resetTourProgress]);
Expand Down
26 changes: 24 additions & 2 deletions src/providers/TourProvider/TourProgressContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export interface TourProgressValue {
completedSteps: ReadonlySet<number>;
isStepComplete(step: number): boolean;
markStepComplete(step: number): void;
maxVisitedStep: number;
recordVisited(step: number): void;
reset(): void;
}

Expand All @@ -20,6 +22,7 @@ export function TourProgressProvider({ children }: { children: ReactNode }) {
const [completedSteps, setCompletedSteps] = useState<Set<number>>(
() => new Set(),
);
const [maxVisitedStep, setMaxVisitedStep] = useState(0);

const isStepComplete = useCallback(
(step: number) => completedSteps.has(step),
Expand All @@ -35,13 +38,32 @@ export function TourProgressProvider({ children }: { children: ReactNode }) {
});
}, []);

const recordVisited = useCallback((step: number) => {
setMaxVisitedStep((prev) => (step > prev ? step : prev));
}, []);

const reset = useCallback(() => {
setCompletedSteps((prev) => (prev.size === 0 ? prev : new Set()));
setMaxVisitedStep(0);
}, []);

const value = useMemo<TourProgressValue>(
() => ({ completedSteps, isStepComplete, markStepComplete, reset }),
[completedSteps, isStepComplete, markStepComplete, reset],
() => ({
completedSteps,
isStepComplete,
markStepComplete,
maxVisitedStep,
recordVisited,
reset,
}),
[
completedSteps,
isStepComplete,
markStepComplete,
maxVisitedStep,
recordVisited,
reset,
],
);

return (
Expand Down
2 changes: 0 additions & 2 deletions src/routes/Dashboard/Learn/Tour.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -291,5 +291,3 @@ function TourMockBackendController() {

return null;
}

// placeholder for empty pr
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ export const EditorMenuBar = observer(function EditorMenuBar() {
<div
className="relative w-full bg-stone-900 px-3 py-1 md:px-4"
style={{ height: `${TOP_NAV_HEIGHT}px` }}
data-tour="editor-top-bar"
>
<InlineStack
align="space-between"
Expand Down
Loading
Loading