Fix curveEditorPopover overwriting existing curve (#151)
* Fix curveEditorPopover overwriting existing curve * Fix existing curve overwrite, other reactivity
This commit is contained in:
parent
417233a7c6
commit
cd9d03076b
2 changed files with 36 additions and 18 deletions
|
@ -102,6 +102,7 @@ const NoResultsFoundContainer = styled.div`
|
||||||
`
|
`
|
||||||
|
|
||||||
enum TextInputMode {
|
enum TextInputMode {
|
||||||
|
init,
|
||||||
user,
|
user,
|
||||||
auto,
|
auto,
|
||||||
}
|
}
|
||||||
|
@ -153,14 +154,16 @@ const CurveEditorPopover: React.FC<IProps> = (props) => {
|
||||||
inputRef.current?.focus()
|
inputRef.current?.focus()
|
||||||
}, [inputRef.current])
|
}, [inputRef.current])
|
||||||
|
|
||||||
const [inputValue, setInputValue] = useState<string>('')
|
const [inputValue, setInputValue] = useState<string>(
|
||||||
|
cssCubicBezierArgsFromHandles(easing),
|
||||||
|
)
|
||||||
|
|
||||||
const onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setTextInputMode(TextInputMode.user)
|
setTextInputMode(TextInputMode.user)
|
||||||
setInputValue(e.target.value)
|
setInputValue(e.target.value)
|
||||||
|
|
||||||
const maybeHandles = handlesFromCssCubicBezierArgs(inputValue)
|
const maybeHandles = handlesFromCssCubicBezierArgs(e.target.value)
|
||||||
if (maybeHandles) setEdit(inputValue)
|
if (maybeHandles) setEdit(e.target.value)
|
||||||
}
|
}
|
||||||
const onSearchKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
|
const onSearchKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||||
setTextInputMode(TextInputMode.user)
|
setTextInputMode(TextInputMode.user)
|
||||||
|
@ -183,7 +186,7 @@ const CurveEditorPopover: React.FC<IProps> = (props) => {
|
||||||
// in user mode, the text input field does not update when the curve
|
// in user mode, the text input field does not update when the curve
|
||||||
// changes so that the user's search is preserved.
|
// changes so that the user's search is preserved.
|
||||||
const [textInputMode, setTextInputMode] = useState<TextInputMode>(
|
const [textInputMode, setTextInputMode] = useState<TextInputMode>(
|
||||||
TextInputMode.auto,
|
TextInputMode.init,
|
||||||
)
|
)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (textInputMode === TextInputMode.auto)
|
if (textInputMode === TextInputMode.auto)
|
||||||
|
@ -191,7 +194,9 @@ const CurveEditorPopover: React.FC<IProps> = (props) => {
|
||||||
}, [trackData])
|
}, [trackData])
|
||||||
|
|
||||||
// `edit` keeps track of the current edited state of the curve.
|
// `edit` keeps track of the current edited state of the curve.
|
||||||
const [edit, setEdit] = useState<CSSCubicBezierArgsString | null>(null)
|
const [edit, setEdit] = useState<CSSCubicBezierArgsString | null>(
|
||||||
|
cssCubicBezierArgsFromHandles(easing),
|
||||||
|
)
|
||||||
// `preview` is used when hovering over a curve to preview it.
|
// `preview` is used when hovering over a curve to preview it.
|
||||||
const [preview, setPreview] = useState<CSSCubicBezierArgsString | null>(null)
|
const [preview, setPreview] = useState<CSSCubicBezierArgsString | null>(null)
|
||||||
|
|
||||||
|
@ -214,18 +219,22 @@ const CurveEditorPopover: React.FC<IProps> = (props) => {
|
||||||
|
|
||||||
////// Preset reactivity //////
|
////// Preset reactivity //////
|
||||||
const displayedPresets = useMemo(() => {
|
const displayedPresets = useMemo(() => {
|
||||||
const presetSearchResults = fuzzy.filter(inputValue, EASING_PRESETS, {
|
|
||||||
extract: (el) => el.label,
|
|
||||||
})
|
|
||||||
const isInputValueAQuery = /^[A-Za-z]/.test(inputValue)
|
const isInputValueAQuery = /^[A-Za-z]/.test(inputValue)
|
||||||
|
|
||||||
return isInputValueAQuery
|
if (isInputValueAQuery) {
|
||||||
? presetSearchResults.map((result) => result.original)
|
return fuzzy
|
||||||
: EASING_PRESETS
|
.filter(inputValue, EASING_PRESETS, {
|
||||||
|
extract: (el) => el.label,
|
||||||
|
})
|
||||||
|
.map((result) => result.original)
|
||||||
|
} else {
|
||||||
|
return EASING_PRESETS
|
||||||
|
}
|
||||||
}, [inputValue])
|
}, [inputValue])
|
||||||
// Use the first preset in the search when the displayed presets change
|
// Use the first preset in the search when the displayed presets change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (displayedPresets[0]) setEdit(displayedPresets[0].value)
|
if (textInputMode === TextInputMode.user && displayedPresets[0])
|
||||||
|
setEdit(displayedPresets[0].value)
|
||||||
}, [displayedPresets])
|
}, [displayedPresets])
|
||||||
|
|
||||||
////// Option grid specification and reactivity //////
|
////// Option grid specification and reactivity //////
|
||||||
|
@ -243,8 +252,10 @@ const CurveEditorPopover: React.FC<IProps> = (props) => {
|
||||||
setPreview(item.value)
|
setPreview(item.value)
|
||||||
const onEasingOptionMouseOut = () => setPreview(null)
|
const onEasingOptionMouseOut = () => setPreview(null)
|
||||||
const onSelectEasingOption = (item: {label: string; value: string}) => {
|
const onSelectEasingOption = (item: {label: string; value: string}) => {
|
||||||
setTextInputMode(TextInputMode.auto)
|
setTempValue(tempTransaction, props, cur, next, item.value)
|
||||||
setEdit(item.value)
|
props.onRequestClose()
|
||||||
|
|
||||||
|
return Outcome.Handled
|
||||||
}
|
}
|
||||||
|
|
||||||
// A map to store all html elements corresponding to easing options
|
// A map to store all html elements corresponding to easing options
|
||||||
|
@ -316,6 +327,10 @@ const CurveEditorPopover: React.FC<IProps> = (props) => {
|
||||||
optionsRef.current?.[grid.currentSelection.label]?.current
|
optionsRef.current?.[grid.currentSelection.label]?.current
|
||||||
maybePresetEl?.focus()
|
maybePresetEl?.focus()
|
||||||
setEdit(grid.currentSelection.value)
|
setEdit(grid.currentSelection.value)
|
||||||
|
const isInputValueAQuery = /^[A-Za-z]/.test(inputValue)
|
||||||
|
if (!isInputValueAQuery) {
|
||||||
|
setInputValue(grid.currentSelection.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [grid.currentSelection])
|
}, [grid.currentSelection])
|
||||||
|
|
||||||
|
|
|
@ -14,13 +14,13 @@ type UIOptionGridOptions<Item> = {
|
||||||
items: Item[]
|
items: Item[]
|
||||||
/** display of items */
|
/** display of items */
|
||||||
renderItem: (value: {
|
renderItem: (value: {
|
||||||
select(): void
|
select(e?: Event): void
|
||||||
/** data item */
|
/** data item */
|
||||||
item: Item
|
item: Item
|
||||||
/** arrow key nav */
|
/** arrow key nav */
|
||||||
isSelected: boolean
|
isSelected: boolean
|
||||||
}) => React.ReactNode
|
}) => React.ReactNode
|
||||||
onSelectItem(item: Item): void
|
onSelectItem(item: Item): Outcome
|
||||||
/** Set a callback for what to do if we try to leave the grid */
|
/** Set a callback for what to do if we try to leave the grid */
|
||||||
canVerticleExit?: (exitSide: 'top' | 'bottom') => Outcome
|
canVerticleExit?: (exitSide: 'top' | 'bottom') => Outcome
|
||||||
}
|
}
|
||||||
|
@ -98,9 +98,12 @@ export function useUIOptionGrid<T>(
|
||||||
options.renderItem({
|
options.renderItem({
|
||||||
isSelected: idx === selectionIndex,
|
isSelected: idx === selectionIndex,
|
||||||
item,
|
item,
|
||||||
select() {
|
select(e) {
|
||||||
setSelectionIndex(idx)
|
setSelectionIndex(idx)
|
||||||
options.onSelectItem(item)
|
if (options.onSelectItem(item) === Outcome.Handled) {
|
||||||
|
e?.preventDefault()
|
||||||
|
e?.stopPropagation()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in a new issue