mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
improvement(tour): align product tour tooltip styling with emcn and fix spotlight overflow (#3854)
This commit is contained in:
@@ -61,7 +61,7 @@ export const navTourSteps: Step[] = [
|
||||
target: '[data-tour="nav-tasks"]',
|
||||
title: 'Tasks',
|
||||
content:
|
||||
'Tasks that work for you. Mothership can create, edit, and delete resource throughout the platform. It can also perform actions on your behalf, like sending emails, creating tasks, and more.',
|
||||
'Tasks that work for you. Mothership can create, edit, and delete resources throughout the platform. It can also perform actions on your behalf, like sending emails, creating tasks, and more.',
|
||||
placement: 'right',
|
||||
disableBeacon: true,
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { createContext, useCallback, useContext, useEffect, useState } from 'react'
|
||||
import { createContext, useCallback, useContext } from 'react'
|
||||
import type { TooltipRenderProps } from 'react-joyride'
|
||||
import { TourTooltip } from '@/components/emcn'
|
||||
|
||||
@@ -59,18 +59,14 @@ export function TourTooltipAdapter({
|
||||
closeProps,
|
||||
}: TooltipRenderProps) {
|
||||
const { isTooltipVisible, isEntrance, totalSteps } = useContext(TourStateContext)
|
||||
const [targetEl, setTargetEl] = useState<HTMLElement | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const { target } = step
|
||||
if (typeof target === 'string') {
|
||||
setTargetEl(document.querySelector<HTMLElement>(target))
|
||||
} else if (target instanceof HTMLElement) {
|
||||
setTargetEl(target)
|
||||
} else {
|
||||
setTargetEl(null)
|
||||
}
|
||||
}, [step])
|
||||
const { target } = step
|
||||
const targetEl =
|
||||
typeof target === 'string'
|
||||
? document.querySelector<HTMLElement>(target)
|
||||
: target instanceof HTMLElement
|
||||
? target
|
||||
: null
|
||||
|
||||
/**
|
||||
* Forwards the Joyride tooltip ref safely, handling both
|
||||
|
||||
@@ -114,6 +114,16 @@ export function useTour({
|
||||
[steps.length, stopTour, cancelPendingTransitions, scheduleReveal]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!run) return
|
||||
const html = document.documentElement
|
||||
const prev = html.style.scrollbarGutter
|
||||
html.style.scrollbarGutter = 'stable'
|
||||
return () => {
|
||||
html.style.scrollbarGutter = prev
|
||||
}
|
||||
}, [run])
|
||||
|
||||
/** Stop the tour when disabled becomes true (e.g. navigating away from the relevant page) */
|
||||
useEffect(() => {
|
||||
if (disabled && run) {
|
||||
|
||||
@@ -1329,8 +1329,11 @@ export const Sidebar = memo(function Sidebar() {
|
||||
!hasOverflowTop && 'border-transparent'
|
||||
)}
|
||||
>
|
||||
<div className='tasks-section flex flex-shrink-0 flex-col' data-tour='nav-tasks'>
|
||||
<div className='flex h-[18px] flex-shrink-0 items-center justify-between px-4'>
|
||||
<div
|
||||
className='tasks-section mx-2 flex flex-shrink-0 flex-col'
|
||||
data-tour='nav-tasks'
|
||||
>
|
||||
<div className='flex h-[18px] flex-shrink-0 items-center justify-between px-2'>
|
||||
<div className='font-base text-[var(--text-icon)] text-small'>All tasks</div>
|
||||
{!isCollapsed && (
|
||||
<div className='flex items-center justify-center gap-2'>
|
||||
@@ -1451,10 +1454,10 @@ export const Sidebar = memo(function Sidebar() {
|
||||
</div>
|
||||
|
||||
<div
|
||||
className='workflows-section relative mt-3.5 flex flex-col'
|
||||
className='workflows-section relative mx-2 mt-3.5 flex flex-col'
|
||||
data-tour='nav-workflows'
|
||||
>
|
||||
<div className='flex h-[18px] flex-shrink-0 items-center justify-between px-4'>
|
||||
<div className='flex h-[18px] flex-shrink-0 items-center justify-between px-2'>
|
||||
<div className='font-base text-[var(--text-icon)] text-small'>Workflows</div>
|
||||
{!isCollapsed && (
|
||||
<div className='flex items-center justify-center gap-2'>
|
||||
|
||||
@@ -45,12 +45,12 @@ function TourCard({
|
||||
return (
|
||||
<>
|
||||
<div className='flex items-center justify-between gap-2 px-4 pt-4 pb-2'>
|
||||
<h3 className='min-w-0 font-medium text-[var(--text-primary)] text-sm leading-none'>
|
||||
<h3 className='min-w-0 font-medium text-[var(--text-primary)] text-small leading-none'>
|
||||
{title}
|
||||
</h3>
|
||||
<Button
|
||||
variant='ghost'
|
||||
className='h-[16px] w-[16px] flex-shrink-0 p-0'
|
||||
className='relative h-[16px] w-[16px] flex-shrink-0 p-0 before:absolute before:inset-[-14px] before:content-[""]'
|
||||
onClick={onClose}
|
||||
aria-label='Close tour'
|
||||
>
|
||||
@@ -60,24 +60,23 @@ function TourCard({
|
||||
</div>
|
||||
|
||||
<div className='px-4 pt-1 pb-3'>
|
||||
<p className='text-[12px] text-[var(--text-secondary)] leading-[1.6]'>{description}</p>
|
||||
<p className='text-[var(--text-secondary)] text-caption leading-[1.6]'>{description}</p>
|
||||
</div>
|
||||
|
||||
<div className='flex items-center justify-between border-[var(--border)] border-t px-4 py-3'>
|
||||
<span className='text-[11px] text-[var(--text-muted)] [font-variant-numeric:tabular-nums]'>
|
||||
<div className='flex items-center justify-between rounded-b-xl border-[var(--border)] border-t bg-[color-mix(in_srgb,var(--surface-3)_50%,transparent)] px-4 py-2'>
|
||||
<span className='text-[var(--text-muted)] text-xs [font-variant-numeric:tabular-nums]'>
|
||||
{step} / {totalSteps}
|
||||
</span>
|
||||
<div className='flex items-center gap-1.5'>
|
||||
<div className={cn(isFirst && 'invisible')}>
|
||||
<Button
|
||||
variant='default'
|
||||
size='sm'
|
||||
onClick={onBack}
|
||||
tabIndex={isFirst ? -1 : undefined}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
variant='default'
|
||||
size='sm'
|
||||
onClick={onBack}
|
||||
tabIndex={isFirst ? -1 : undefined}
|
||||
className={cn(isFirst && 'invisible')}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
<Button variant='tertiary' size='sm' onClick={onNext}>
|
||||
{isLast ? 'Done' : 'Next'}
|
||||
</Button>
|
||||
@@ -156,7 +155,7 @@ function TourTooltip({
|
||||
const isCentered = placement === 'center'
|
||||
|
||||
const cardClasses = cn(
|
||||
'w-[260px] overflow-hidden rounded-[8px] bg-[var(--bg)]',
|
||||
'w-[260px] overflow-hidden rounded-xl bg-[var(--bg)]',
|
||||
isEntrance && 'animate-tour-tooltip-in motion-reduce:animate-none',
|
||||
className
|
||||
)
|
||||
@@ -181,7 +180,7 @@ function TourTooltip({
|
||||
<div
|
||||
className={cn(
|
||||
cardClasses,
|
||||
'pointer-events-auto relative border border-[var(--border)] shadow-sm'
|
||||
'pointer-events-auto relative shadow-overlay ring-1 ring-foreground/10'
|
||||
)}
|
||||
>
|
||||
{cardContent}
|
||||
@@ -202,10 +201,7 @@ function TourTooltip({
|
||||
sideOffset={10}
|
||||
collisionPadding={12}
|
||||
avoidCollisions
|
||||
className='z-[10000300] outline-none'
|
||||
style={{
|
||||
filter: 'drop-shadow(0 0 0.5px var(--border)) drop-shadow(0 1px 2px rgba(0,0,0,0.1))',
|
||||
}}
|
||||
className='z-[10000300] outline-none drop-shadow-tour'
|
||||
onOpenAutoFocus={(e) => e.preventDefault()}
|
||||
onCloseAutoFocus={(e) => e.preventDefault()}
|
||||
>
|
||||
|
||||
@@ -126,6 +126,12 @@ export default {
|
||||
'brand-inset': 'var(--shadow-brand-inset)',
|
||||
card: 'var(--shadow-card)',
|
||||
},
|
||||
dropShadow: {
|
||||
tour: [
|
||||
'0 0 0.5px color-mix(in srgb, var(--text-primary) 10%, transparent)',
|
||||
'0 4px 12px rgba(0,0,0,0.1)',
|
||||
],
|
||||
},
|
||||
transitionProperty: {
|
||||
width: 'width',
|
||||
left: 'left',
|
||||
|
||||
Reference in New Issue
Block a user