@@ -40,6 +40,7 @@ import { Keyframes } from "./animation-keyframes";
4040import { humanizeString } from "~/shared/string-utils" ;
4141import { Link2Icon , Link2UnlinkedIcon } from "@webstudio-is/icons" ;
4242import { $availableUnitVariables } from "~/builder/features/style-panel/shared/model" ;
43+ import { AnimationRanges } from "./animation-ranges" ;
4344
4445const fillModeDescriptions : Record <
4546 NonNullable < ViewAnimation [ "timing" ] [ "fill" ] > ,
@@ -469,22 +470,46 @@ export const AnimationPanelContent = ({
469470 } }
470471 />
471472 </ Grid >
473+
472474 < Grid
473475 gap = { 1 }
474476 align = { "center" }
475477 css = { {
476- gridTemplateColumns : "1fr 16px 1fr" ,
478+ gridTemplateAreas : `
479+ "rangeEndLabel . . animation"
480+ "rangeEndName rangeEndPercentage connect animation"
481+ "rangeStartLabel . connect animation"
482+ "rangeStartName rangeStartPercentage connect animation"
483+ "durationLabel durationValue . animation"
484+ ` ,
485+ gridTemplateColumns : "1.5fr 1fr 16px 34px" ,
477486 paddingInline : theme . panel . paddingInline ,
478487 flexShrink : 0 ,
479488 } }
480489 >
481- < Label htmlFor = { fieldIds . rangeStartName } > Range Start</ Label >
482- < div />
483- < Label disabled = { ! isRangeEndEnabled } htmlFor = { fieldIds . rangeEndName } >
490+ < Label
491+ css = { {
492+ gridArea : "rangeStartLabel" ,
493+ } }
494+ htmlFor = { fieldIds . rangeStartName }
495+ >
496+ Range Start
497+ </ Label >
498+
499+ < Label
500+ css = { {
501+ gridArea : "rangeEndLabel" ,
502+ } }
503+ disabled = { ! isRangeEndEnabled }
504+ htmlFor = { fieldIds . rangeEndName }
505+ >
484506 Range End
485507 </ Label >
486508
487509 < Select
510+ css = { {
511+ gridArea : "rangeStartName" ,
512+ } }
488513 id = { fieldIds . rangeStartName }
489514 options = { timelineRangeNames }
490515 getLabel = { humanizeString }
@@ -550,7 +575,11 @@ export const AnimationPanelContent = ({
550575 ) ;
551576 } }
552577 />
553- < Grid >
578+ < Grid
579+ css = { {
580+ gridArea : "connect" ,
581+ } }
582+ >
554583 < EnhancedTooltip
555584 content = { isLinked ? "Unlink range names" : "Link range names" }
556585 >
@@ -582,6 +611,9 @@ export const AnimationPanelContent = ({
582611 </ EnhancedTooltip >
583612 </ Grid >
584613 < Select
614+ css = { {
615+ gridArea : "rangeEndName" ,
616+ } }
585617 id = { fieldIds . rangeEndName }
586618 disabled = { ! isRangeEndEnabled }
587619 options = { timelineRangeNames }
@@ -648,105 +680,114 @@ export const AnimationPanelContent = ({
648680 } }
649681 />
650682
651- < RangeValueInput
652- id = { fieldIds . rangeStartValue }
653- value = {
654- value . timing . rangeStart ?. [ 1 ] ?? {
655- type : "unit" ,
656- value : 0 ,
657- unit : "%" ,
658- }
659- }
660- onChange = { ( rangeStart , isEphemeral ) => {
661- if ( rangeStart === undefined && isEphemeral ) {
662- handleChange ( undefined , true ) ;
663- return ;
683+ < Box css = { { gridArea : "rangeStartPercentage" } } >
684+ < RangeValueInput
685+ id = { fieldIds . rangeStartValue }
686+ value = {
687+ value . timing . rangeStart ?. [ 1 ] ?? {
688+ type : "unit" ,
689+ value : 0 ,
690+ unit : "%" ,
691+ }
664692 }
693+ onChange = { ( rangeStart , isEphemeral ) => {
694+ if ( rangeStart === undefined && isEphemeral ) {
695+ handleChange ( undefined , true ) ;
696+ return ;
697+ }
665698
666- const defaultTimelineRangeName = timelineRangeNames [ 0 ] ! ;
667-
668- handleChange (
669- {
670- ...value ,
671- timing : {
672- ...value . timing ,
673- rangeStart : [
674- value . timing . rangeStart ?. [ 0 ] ?? defaultTimelineRangeName ,
675- rangeStart ,
676- ] ,
699+ const defaultTimelineRangeName = timelineRangeNames [ 0 ] ! ;
700+
701+ handleChange (
702+ {
703+ ...value ,
704+ timing : {
705+ ...value . timing ,
706+ rangeStart : [
707+ value . timing . rangeStart ?. [ 0 ] ?? defaultTimelineRangeName ,
708+ rangeStart ,
709+ ] ,
710+ } ,
677711 } ,
678- } ,
679- isEphemeral
680- ) ;
681- } }
682- />
683- < div />
684- < RangeValueInput
685- id = { fieldIds . rangeEndValue }
686- disabled = { ! isRangeEndEnabled }
687- value = {
688- value . timing . rangeEnd ?. [ 1 ] ?? {
689- type : "unit" ,
690- value : 0 ,
691- unit : "%" ,
692- }
693- }
694- onChange = { ( rangeEnd , isEphemeral ) => {
695- if ( rangeEnd === undefined && isEphemeral ) {
696- handleChange ( undefined , true ) ;
697- return ;
712+ isEphemeral
713+ ) ;
714+ } }
715+ />
716+ </ Box >
717+ < Box css = { { gridArea : "rangeEndPercentage" } } >
718+ < RangeValueInput
719+ id = { fieldIds . rangeEndValue }
720+ disabled = { ! isRangeEndEnabled }
721+ value = {
722+ value . timing . rangeEnd ?. [ 1 ] ?? {
723+ type : "unit" ,
724+ value : 0 ,
725+ unit : "%" ,
726+ }
698727 }
728+ onChange = { ( rangeEnd , isEphemeral ) => {
729+ if ( rangeEnd === undefined && isEphemeral ) {
730+ handleChange ( undefined , true ) ;
731+ return ;
732+ }
699733
700- const defaultTimelineRangeName = timelineRangeNames [ 0 ] ! ;
701-
702- handleChange (
703- {
704- ...value ,
705- timing : {
706- ...value . timing ,
707- rangeEnd : [
708- value . timing . rangeEnd ?. [ 0 ] ?? defaultTimelineRangeName ,
709- rangeEnd ,
710- ] ,
734+ const defaultTimelineRangeName = timelineRangeNames [ 0 ] ! ;
735+
736+ handleChange (
737+ {
738+ ...value ,
739+ timing : {
740+ ...value . timing ,
741+ rangeEnd : [
742+ value . timing . rangeEnd ?. [ 0 ] ?? defaultTimelineRangeName ,
743+ rangeEnd ,
744+ ] ,
745+ } ,
711746 } ,
712- } ,
713- isEphemeral
714- ) ;
747+ isEphemeral
748+ ) ;
749+ } }
750+ />
751+ </ Box >
752+
753+ < Label
754+ css = { {
755+ gridArea : "durationLabel" ,
715756 } }
716- />
717- </ Grid >
757+ htmlFor = { fieldIds . duration }
758+ >
759+ Duration
760+ </ Label >
718761
719- < Grid
720- gap = { 1 }
721- align = { "center" }
722- css = { {
723- gridTemplateColumns : "1fr 16px 1fr" ,
724- paddingInline : theme . panel . paddingInline ,
725- } }
726- >
727- < Label htmlFor = { fieldIds . duration } > Duration</ Label >
728- < div />
729- < DurationInput
730- id = { fieldIds . duration }
731- value = { value . timing . duration }
732- onChange = { ( duration , isEphemeral ) => {
733- if ( duration === undefined && isEphemeral ) {
734- handleChange ( undefined , true ) ;
735- return ;
736- }
762+ < Box css = { { gridArea : "durationValue" } } >
763+ < DurationInput
764+ id = { fieldIds . duration }
765+ value = { value . timing . duration }
766+ onChange = { ( duration , isEphemeral ) => {
767+ if ( duration === undefined && isEphemeral ) {
768+ handleChange ( undefined , true ) ;
769+ return ;
770+ }
737771
738- handleChange (
739- {
740- ...value ,
741- timing : {
742- ...value . timing ,
743- duration,
772+ handleChange (
773+ {
774+ ...value ,
775+ timing : {
776+ ...value . timing ,
777+ duration,
778+ } ,
744779 } ,
745- } ,
746- isEphemeral
747- ) ;
748- } }
749- />
780+ isEphemeral
781+ ) ;
782+ } }
783+ />
784+ </ Box >
785+ < Grid css = { { gridArea : "animation" , alignSelf : "stretch" } } >
786+ < AnimationRanges
787+ rangeStart = { value . timing . rangeStart }
788+ rangeEnd = { value . timing . rangeEnd }
789+ />
790+ </ Grid >
750791 </ Grid >
751792
752793 < Keyframes
0 commit comments