From cafc7ce350e8a35a2811daeb15a25c9d830d1b46 Mon Sep 17 00:00:00 2001 From: Philipp Fromme Date: Tue, 10 Dec 2024 13:37:12 +0100 Subject: [PATCH] feat: add dropdown --- src/assets/properties-panel.css | 36 ++++++++ src/components/entries/TextField.js | 131 ++++++++++++++++++++++++---- 2 files changed, 149 insertions(+), 18 deletions(-) diff --git a/src/assets/properties-panel.css b/src/assets/properties-panel.css index b0311ace..30c4f1f0 100644 --- a/src/assets/properties-panel.css +++ b/src/assets/properties-panel.css @@ -499,6 +499,42 @@ font-family: inherit; } +.bio-properties-panel-textfield-dropdown-wrapper { + position: relative; +} + +.bio-properties-panel-textfield-dropdown { + display: none; + position: absolute; + top: 100%; + left: 0; + width: 100%; + z-index: 100; + background-color: var(--color-grey-225-10-95); + border: 1px solid var(--color-grey-225-10-75); +} + +.bio-properties-panel-textfield-dropdown-open { + display: block; +} + +.bio-properties-panel-textfield-dropdown-options { + list-style: none; + padding: 0; + margin: 0; +} + +.bio-properties-panel-textfield-dropdown-option { + padding: 2px 6px; +} + +.bio-properties-panel-textfield-dropdown-option:focus, +.bio-properties-panel-textfield-dropdown-option:hover, +.bio-properties-panel-textfield-dropdown-option-selected { + background-color: var(--color-blue-205-100-50); + color: var(--color-white); +} + .bio-properties-panel-input[type=number], select.bio-properties-panel-input, textarea.bio-properties-panel-input, diff --git a/src/components/entries/TextField.js b/src/components/entries/TextField.js index 15012ce8..0b12cee5 100644 --- a/src/components/entries/TextField.js +++ b/src/components/entries/TextField.js @@ -9,12 +9,13 @@ import { import classnames from 'classnames'; -import { isFunction } from 'min-dash'; +import { isFunction, set } from 'min-dash'; import { useError, useShowEntryEvent } from '../../hooks'; +import classNames from 'classnames'; function Textfield(props) { @@ -24,14 +25,32 @@ function Textfield(props) { id, label, onInput, - onFocus, - onBlur, + onFocus: _onFocus, + onBlur: _onBlur, placeholder, value = '', - tooltip + tooltip, + dropdown = { + options: [ + { + value: 'foo' + }, + { + value: 'bar' + }, + { + value: 'baz' + } + ], + text: 'Foo bar!' + } } = props; const [ localValue, setLocalValue ] = useState(value || ''); + const [ dropdownOptions, setDropdownOptions ] = useState([]); + const [ dropdownOpen, setDropdownOpen ] = useState(false); + const [ dropdownOptionSelected, setDropdownOptionSelected ] = useState(0); + const [ isFocused, setIsFocused ] = useState(false); const ref = useShowEntryEvent(id); @@ -44,6 +63,31 @@ function Textfield(props) { setLocalValue(e.target.value); }; + const onFocus = e => { + setIsFocused(true); + isFunction(_onFocus) && _onFocus(e); + }; + + const onBlur = e => { + setIsFocused(false); + isFunction(_onBlur) && _onBlur(e); + }; + + const onKeydown = e => { + if (isFocused && e.key === 'Escape') { + setDropdownOpen(false); + } else if (isFocused && e.key === 'ArrowUp') { + e.preventDefault(); + setDropdownOptionSelected(Math.max(0, dropdownOptionSelected - 1)); + } else if (isFocused && e.key === 'ArrowDown') { + e.preventDefault(); + setDropdownOptionSelected(Math.min(dropdownOptions.length - 1, dropdownOptionSelected + 1)); + } else if (isFocused && e.key === 'Enter') { + handleInput({ target: { value: dropdownOptions[dropdownOptionSelected].value } }); + setDropdownOpen(false); + } + }; + useEffect(() => { if (value === localValue) { return; @@ -52,6 +96,27 @@ function Textfield(props) { setLocalValue(value); }, [ value ]); + useEffect(() => { + if (!dropdown) { + return; + } + + setDropdownOptions(dropdown.options.filter(option => option.value.startsWith(localValue))); + }, [ localValue ]); + + useEffect(() => { + setDropdownOpen(dropdownOptions.length > 0 && isFocused); + setDropdownOptionSelected(Math.max(Math.min(dropdownOptions.length - 1, dropdownOptionSelected), 0)); + }, [ dropdownOptions, isFocused ]); + + useEffect(() => { + if (!dropdownOpen) { + setDropdownOptionSelected(0); + } + }, [ dropdownOpen ]); + + console.log('selected', dropdownOptionSelected); + return (
- +
+ + { + dropdownOpen &&
+ { + dropdownOptions.length &&
    + { + dropdownOptions.map((option, index) => { + return
  • handleInput({ target: { value: option.value } }) } + > + { option.value } +
  • ; + }) + } +
+ } +
+ } +
); }