diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/CurveEditorPopover/CurveEditorPopover.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/CurveEditorPopover/CurveEditorPopover.tsx index 5b67a9d..8b986df 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/CurveEditorPopover/CurveEditorPopover.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/CurveEditorPopover/CurveEditorPopover.tsx @@ -102,6 +102,7 @@ const NoResultsFoundContainer = styled.div` ` enum TextInputMode { + init, user, auto, } @@ -153,14 +154,16 @@ const CurveEditorPopover: React.FC = (props) => { inputRef.current?.focus() }, [inputRef.current]) - const [inputValue, setInputValue] = useState('') + const [inputValue, setInputValue] = useState( + cssCubicBezierArgsFromHandles(easing), + ) const onInputChange = (e: React.ChangeEvent) => { setTextInputMode(TextInputMode.user) setInputValue(e.target.value) - const maybeHandles = handlesFromCssCubicBezierArgs(inputValue) - if (maybeHandles) setEdit(inputValue) + const maybeHandles = handlesFromCssCubicBezierArgs(e.target.value) + if (maybeHandles) setEdit(e.target.value) } const onSearchKeyDown = (e: KeyboardEvent) => { setTextInputMode(TextInputMode.user) @@ -183,7 +186,7 @@ const CurveEditorPopover: React.FC = (props) => { // in user mode, the text input field does not update when the curve // changes so that the user's search is preserved. const [textInputMode, setTextInputMode] = useState( - TextInputMode.auto, + TextInputMode.init, ) useEffect(() => { if (textInputMode === TextInputMode.auto) @@ -191,7 +194,9 @@ const CurveEditorPopover: React.FC = (props) => { }, [trackData]) // `edit` keeps track of the current edited state of the curve. - const [edit, setEdit] = useState(null) + const [edit, setEdit] = useState( + cssCubicBezierArgsFromHandles(easing), + ) // `preview` is used when hovering over a curve to preview it. const [preview, setPreview] = useState(null) @@ -214,18 +219,22 @@ const CurveEditorPopover: React.FC = (props) => { ////// Preset reactivity ////// const displayedPresets = useMemo(() => { - const presetSearchResults = fuzzy.filter(inputValue, EASING_PRESETS, { - extract: (el) => el.label, - }) const isInputValueAQuery = /^[A-Za-z]/.test(inputValue) - return isInputValueAQuery - ? presetSearchResults.map((result) => result.original) - : EASING_PRESETS + if (isInputValueAQuery) { + return fuzzy + .filter(inputValue, EASING_PRESETS, { + extract: (el) => el.label, + }) + .map((result) => result.original) + } else { + return EASING_PRESETS + } }, [inputValue]) // Use the first preset in the search when the displayed presets change useEffect(() => { - if (displayedPresets[0]) setEdit(displayedPresets[0].value) + if (textInputMode === TextInputMode.user && displayedPresets[0]) + setEdit(displayedPresets[0].value) }, [displayedPresets]) ////// Option grid specification and reactivity ////// @@ -243,8 +252,10 @@ const CurveEditorPopover: React.FC = (props) => { setPreview(item.value) const onEasingOptionMouseOut = () => setPreview(null) const onSelectEasingOption = (item: {label: string; value: string}) => { - setTextInputMode(TextInputMode.auto) - setEdit(item.value) + setTempValue(tempTransaction, props, cur, next, item.value) + props.onRequestClose() + + return Outcome.Handled } // A map to store all html elements corresponding to easing options @@ -316,6 +327,10 @@ const CurveEditorPopover: React.FC = (props) => { optionsRef.current?.[grid.currentSelection.label]?.current maybePresetEl?.focus() setEdit(grid.currentSelection.value) + const isInputValueAQuery = /^[A-Za-z]/.test(inputValue) + if (!isInputValueAQuery) { + setInputValue(grid.currentSelection.value) + } } }, [grid.currentSelection]) diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/CurveEditorPopover/useUIOptionGrid.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/CurveEditorPopover/useUIOptionGrid.tsx index 3a945f9..8918af7 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/CurveEditorPopover/useUIOptionGrid.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/CurveEditorPopover/useUIOptionGrid.tsx @@ -14,13 +14,13 @@ type UIOptionGridOptions = { items: Item[] /** display of items */ renderItem: (value: { - select(): void + select(e?: Event): void /** data item */ item: Item /** arrow key nav */ isSelected: boolean }) => React.ReactNode - onSelectItem(item: Item): void + onSelectItem(item: Item): Outcome /** Set a callback for what to do if we try to leave the grid */ canVerticleExit?: (exitSide: 'top' | 'bottom') => Outcome } @@ -98,9 +98,12 @@ export function useUIOptionGrid( options.renderItem({ isSelected: idx === selectionIndex, item, - select() { + select(e) { setSelectionIndex(idx) - options.onSelectItem(item) + if (options.onSelectItem(item) === Outcome.Handled) { + e?.preventDefault() + e?.stopPropagation() + } }, }), ),