@@ -5,6 +5,7 @@ import { mergeRefs } from "@react-aria/utils";
55import { useStore } from "@nanostores/react" ;
66import {
77 Box ,
8+ keyframes ,
89 rawTheme ,
910 ScrollArea ,
1011 SmallIconButton ,
@@ -19,7 +20,10 @@ import {
1920 TreeSortableItem ,
2021 type TreeDropTarget ,
2122} from "@webstudio-is/design-system" ;
22- import { showAttribute } from "@webstudio-is/react-sdk" ;
23+ import {
24+ animationCanPlayOnCanvasAttribute ,
25+ showAttribute ,
26+ } from "@webstudio-is/react-sdk" ;
2327import {
2428 ROOT_INSTANCE_ID ,
2529 collectionComponent ,
@@ -49,6 +53,7 @@ import {
4953 $selectedInstanceSelector ,
5054 getIndexedInstanceId ,
5155 type ItemDropTarget ,
56+ $propValuesByInstanceSelectorWithMemoryProps ,
5257} from "~/shared/nano-states" ;
5358import type { InstanceSelector } from "~/shared/tree-utils" ;
5459import { serverSyncStore } from "~/shared/sync" ;
@@ -282,12 +287,27 @@ const handleExpand = (item: TreeItem, isExpanded: boolean, all: boolean) => {
282287 $expandedItems . set ( expandedItems ) ;
283288} ;
284289
290+ const pulse = keyframes ( {
291+ "0%" : { fillOpacity : 0 } ,
292+ "100%" : { fillOpacity : 1 } ,
293+ } ) ;
294+
295+ const AnimatedEyeOpenIcon = styled ( EyeOpenIcon , {
296+ "& .ws-eye-open-pupil" : {
297+ transformOrigin : "center" ,
298+ animation : `${ pulse } 1.5s ease-in-out infinite alternate` ,
299+ fill : "currentColor" ,
300+ } ,
301+ } ) ;
302+
285303const ShowToggle = ( {
286304 instance,
287305 value,
306+ isAnimating,
288307} : {
289308 instance : Instance ;
290309 value : boolean ;
310+ isAnimating : boolean ;
291311} ) => {
292312 // descendant component is not actually rendered
293313 // but affects styling of nested elements
@@ -315,18 +335,45 @@ const ShowToggle = ({
315335 }
316336 } ) ;
317337 } ;
338+
339+ const EyeIcon = isAnimating ? AnimatedEyeOpenIcon : EyeOpenIcon ;
340+
318341 return (
319342 < Tooltip
320343 // If you are changing it, change the other one too
321- content = "Removes the instance from the DOM. Breakpoints have no effect on this setting."
344+ content = {
345+ < Text >
346+ Removes the instance from the DOM. Breakpoints have no effect on this
347+ setting.
348+ { isAnimating && value && (
349+ < >
350+ < br />
351+ < Text css = { { color : theme . colors . foregroundPrimary } } >
352+ Animation is running on canvas.
353+ </ Text >
354+ </ >
355+ ) }
356+ </ Text >
357+ }
322358 disableHoverableContent
323359 variant = "wrapped"
324360 >
325361 < SmallIconButton
362+ css = {
363+ value && isAnimating
364+ ? {
365+ color : theme . colors . foregroundPrimary ,
366+ "&:hover" : {
367+ color : theme . colors . foregroundPrimary ,
368+ filter : "brightness(80%)" ,
369+ } ,
370+ }
371+ : undefined
372+ }
326373 tabIndex = { - 1 }
327374 aria-label = "Show"
328375 onClick = { toggleShow }
329- icon = { value ? < EyeOpenIcon /> : < EyeClosedIcon /> }
376+ icon = { value ? < EyeIcon /> : < EyeClosedIcon /> }
330377 />
331378 </ Tooltip >
332379 ) ;
@@ -521,7 +568,11 @@ export const NavigatorTree = () => {
521568 const selectedKey = selectedInstanceSelector ?. join ( ) ;
522569 const hoveredInstanceSelector = useStore ( $hoveredInstanceSelector ) ;
523570 const hoveredKey = hoveredInstanceSelector ?. join ( ) ;
524- const propValuesByInstanceSelector = useStore ( $propValuesByInstanceSelector ) ;
571+ const propValuesByInstanceSelectorWithMemoryProps = useStore (
572+ $propValuesByInstanceSelectorWithMemoryProps
573+ ) ;
574+ const { propsByInstanceId } = useStore ( $propsIndex ) ;
575+
525576 const metas = useStore ( $registeredComponentMetas ) ;
526577 const editingItemSelector = useStore ( $editingItemSelector ) ;
527578 const dragAndDropState = useStore ( $dragAndDropState ) ;
@@ -608,14 +659,30 @@ export const NavigatorTree = () => {
608659 { flatTree . map ( ( item ) => {
609660 const level = item . visibleAncestors . length - 1 ;
610661 const key = item . selector . join ( ) ;
611- const propValues = propValuesByInstanceSelector . get (
662+ const propValues = propValuesByInstanceSelectorWithMemoryProps . get (
612663 getInstanceKey ( item . selector )
613664 ) ;
614665 const show = Boolean ( propValues ?. get ( showAttribute ) ?? true ) ;
666+
667+ // Hook memory prop
668+ const isAnimationSelected =
669+ propValues ?. get ( animationCanPlayOnCanvasAttribute ) === true ;
670+
671+ const props = propsByInstanceId . get ( item . instance . id ) ;
672+ const actionProp = props ?. find (
673+ ( prop ) => prop . type === "animationAction"
674+ ) ;
675+
676+ const isAnimationPinned = actionProp ?. value ?. isPinned === true ;
677+
678+ const isAnimating = isAnimationSelected || isAnimationPinned ;
679+
615680 const meta = metas . get ( item . instance . component ) ;
681+
616682 if ( meta === undefined ) {
617683 return ;
618684 }
685+
619686 return (
620687 < TreeSortableItem
621688 key = { key }
@@ -669,6 +736,7 @@ export const NavigatorTree = () => {
669736 isSelected = { selectedKey === key }
670737 isHighlighted = { hoveredKey === key || dropTargetKey === key }
671738 isExpanded = { item . isExpanded }
739+ isActionVisible = { isAnimating }
672740 onExpand = { ( isExpanded , all ) =>
673741 handleExpand ( item , isExpanded , all )
674742 }
@@ -696,7 +764,13 @@ export const NavigatorTree = () => {
696764 }
697765 } ,
698766 } }
699- action = { < ShowToggle instance = { item . instance } value = { show } /> }
767+ action = {
768+ < ShowToggle
769+ instance = { item . instance }
770+ value = { show }
771+ isAnimating = { isAnimating }
772+ />
773+ }
700774 >
701775 < TreeNodeContent
702776 meta = { meta }
0 commit comments