From acc0a9ad199dbe8e39bee261fed6af9c31c3d75d Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Wed, 13 Sep 2023 10:50:20 +0200 Subject: [PATCH] #661 - Fix dropdowns --- CHANGELOG.md | 1 + .../components/Fields/ChoiceField.tsx | 15 ++++-- .../Fields/ContentTypeRelationshipField.tsx | 15 ++++-- .../components/Fields/DataFileField.tsx | 13 +++-- src/panelWebView/components/TagPicker.tsx | 46 +++------------- src/panelWebView/hooks/useDropdownStyle.tsx | 53 +++++++++++++++++++ src/panelWebView/styles.css | 5 ++ 7 files changed, 99 insertions(+), 49 deletions(-) create mode 100644 src/panelWebView/hooks/useDropdownStyle.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index a9590ca7..91aa69a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ ### 🐞 Fixes - [#660](https://github.com/estruyf/vscode-front-matter/issues/660): Allow only to select unique content relationship values +- [#661](https://github.com/estruyf/vscode-front-matter/issues/661): Fixing the dropdowns when used at the bottom of a collapsible group ## [9.2.0] - 2023-09-11 diff --git a/src/panelWebView/components/Fields/ChoiceField.tsx b/src/panelWebView/components/Fields/ChoiceField.tsx index 7ce301e3..cbbd4777 100644 --- a/src/panelWebView/components/Fields/ChoiceField.tsx +++ b/src/panelWebView/components/Fields/ChoiceField.tsx @@ -9,6 +9,7 @@ import { FieldTitle } from './FieldTitle'; import { FieldMessage } from './FieldMessage'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../../localization'; +import useDropdownStyle from '../../hooks/useDropdownStyle'; export interface IChoiceFieldProps extends BaseFieldProps { choices: string[] | Choice[]; @@ -27,6 +28,8 @@ export const ChoiceField: React.FunctionComponent = ({ }: React.PropsWithChildren) => { const [crntSelected, setCrntSelected] = React.useState(value); const dsRef = React.useRef | null>(null); + const inputRef = React.useRef(null); + const { getDropdownStyle } = useDropdownStyle(inputRef as any); const onValueChange = (txtValue: string) => { if (multiSelect) { @@ -105,6 +108,7 @@ export const ChoiceField: React.FunctionComponent = ({ {({ getToggleButtonProps, getItemProps, getMenuProps, isOpen, getRootProps }) => (
    - {isOpen - ? availableChoices.map((choice, index) => ( + { + availableChoices.map((choice, index) => (
  • = ({ )}
  • )) - : null} + }
)} diff --git a/src/panelWebView/components/Fields/ContentTypeRelationshipField.tsx b/src/panelWebView/components/Fields/ContentTypeRelationshipField.tsx index a59dd32b..da353a8d 100644 --- a/src/panelWebView/components/Fields/ContentTypeRelationshipField.tsx +++ b/src/panelWebView/components/Fields/ContentTypeRelationshipField.tsx @@ -11,6 +11,7 @@ import { CommandToCode } from '../../CommandToCode'; import { Page } from '../../../dashboardWebView/models'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../../localization'; +import useDropdownStyle from '../../hooks/useDropdownStyle'; export interface IContentTypeRelationshipFieldProps extends BaseFieldProps { contentTypeName?: string; @@ -34,6 +35,8 @@ export const ContentTypeRelationshipField: React.FunctionComponent([]); const [crntSelected, setCrntSelected] = React.useState(value); const dsRef = React.useRef | null>(null); + const inputRef = React.useRef(null); + const { getDropdownStyle } = useDropdownStyle(inputRef as any); const onValueChange = (txtValue: string) => { if (multiSelect) { @@ -143,6 +146,7 @@ export const ContentTypeRelationshipField: React.FunctionComponent (
)} diff --git a/src/panelWebView/components/Fields/DataFileField.tsx b/src/panelWebView/components/Fields/DataFileField.tsx index 8bfef577..2cf873dd 100644 --- a/src/panelWebView/components/Fields/DataFileField.tsx +++ b/src/panelWebView/components/Fields/DataFileField.tsx @@ -9,6 +9,7 @@ import { FieldTitle } from './FieldTitle'; import { FieldMessage } from './FieldMessage'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../../localization'; +import useDropdownStyle from '../../hooks/useDropdownStyle'; export interface IDataFileFieldProps { label: string; @@ -36,6 +37,8 @@ export const DataFileField: React.FunctionComponent = ({ const [dataEntries, setDataEntries] = useState(null); const [crntSelected, setCrntSelected] = React.useState(); const dsRef = React.useRef | null>(null); + const inputRef = React.useRef(null); + const { getDropdownStyle } = useDropdownStyle(inputRef as any); const onValueChange = useCallback( (txtValue: string) => { @@ -149,6 +152,7 @@ export const DataFileField: React.FunctionComponent = ({ {({ getToggleButtonProps, getItemProps, getMenuProps, isOpen, getRootProps }) => (
)} diff --git a/src/panelWebView/components/TagPicker.tsx b/src/panelWebView/components/TagPicker.tsx index 004b4475..8eec3679 100644 --- a/src/panelWebView/components/TagPicker.tsx +++ b/src/panelWebView/components/TagPicker.tsx @@ -15,6 +15,7 @@ import { PanelSettingsAtom } from '../state'; import { SparklesIcon } from '@heroicons/react/outline'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../localization'; +import useDropdownStyle from '../hooks/useDropdownStyle'; export interface ITagPickerProps { type: TagType; @@ -58,6 +59,7 @@ const TagPicker: React.FunctionComponent = ({ const [inputValue, setInputValue] = React.useState(''); const prevSelected = usePrevious(crntSelected); const inputRef = React.useRef(null); + const { getDropdownStyle } = useDropdownStyle(inputRef as any); const dsRef = React.useRef | null>(null); const settings = useRecoilValue(PanelSettingsAtom); const [loading, setLoading] = React.useState(false); @@ -277,38 +279,6 @@ const TagPicker: React.FunctionComponent = ({ ); }, [settings?.aiEnabled, label, type]); - const dropdownStyle = useCallback((isOpen) => { - if (isOpen && inputRef.current) { - const wrapper = inputRef.current.closest(".collapsible__body"); - const dropdown = inputRef.current.parentElement?.parentElement?.querySelector('.article__tags__dropbox'); - - if (!wrapper || !dropdown) { - return undefined; - } - - const wrapperStyles = getComputedStyle(wrapper); - const padding = parseInt(wrapperStyles.paddingTop) + parseInt(wrapperStyles.paddingBottom); - const wrapperHeight = wrapper.clientHeight - padding; - - const tagPickerElm = inputRef.current.parentElement?.parentElement; - const dropdownHeight = dropdown?.clientHeight; - - if (!tagPickerElm || !dropdownHeight) { - return undefined; - } - - const tagPickerTop = tagPickerElm.offsetTop; - - const fullHeight = tagPickerTop + dropdownHeight; - - if (fullHeight > wrapperHeight) { - return "calc(100% - 38px)"; - } - } - - return undefined; - }, [inputRef]); - useEffect(() => { setTimeout(() => { triggerFocus(); @@ -322,7 +292,7 @@ const TagPicker: React.FunctionComponent = ({ }, [crntSelected]); return ( -
+
{ loading && (
@@ -405,19 +375,19 @@ const TagPicker: React.FunctionComponent = ({
    - {isOpen - ? options + { + options .filter((option) => filterList(option, inputValue)) .map((item, index) => (
  • {item}
  • )) - : null} + }
)} diff --git a/src/panelWebView/hooks/useDropdownStyle.tsx b/src/panelWebView/hooks/useDropdownStyle.tsx new file mode 100644 index 00000000..ff924f63 --- /dev/null +++ b/src/panelWebView/hooks/useDropdownStyle.tsx @@ -0,0 +1,53 @@ +import { useCallback } from 'react'; + +export default function useDropdownStyle(inputRef: React.MutableRefObject) { + const bottomStyle = "calc(100% - 38px)"; + const listItemHeight = 28; + + const getDropdownStyle = useCallback((isOpen) => { + if (isOpen && inputRef.current) { + const wrapper = inputRef.current.closest(".collapsible__body"); + const dropdown = inputRef.current.parentElement?.parentElement?.querySelector('.field_dropdown'); + + if (!wrapper || !dropdown) { + return undefined; + } + + const wrapperStyles = getComputedStyle(wrapper); + const padding = parseInt(wrapperStyles.paddingTop) + parseInt(wrapperStyles.paddingBottom); + const wrapperHeight = wrapper.clientHeight - padding; + + const dropdownElm: HTMLElement | null = inputRef.current.closest(".metadata_field"); + const hasStyles = dropdown.getAttribute("style")?.includes(bottomStyle); + + if (hasStyles) { + return bottomStyle; + } + + let dropdownHeight = dropdown?.clientHeight; + if (dropdownHeight === 0) { + const listItems = dropdown?.querySelectorAll('li'); + if (listItems && listItems.length > 0) { + dropdownHeight = listItems.length * listItemHeight; + } + } + + if (!dropdownElm || !dropdownHeight) { + return undefined; + } + + const tagPickerTop = dropdownElm.offsetTop; + + const fullHeight = tagPickerTop + dropdownHeight; + if (fullHeight > wrapperHeight) { + return bottomStyle; + } + } + + return undefined; + }, [inputRef]); + + return { + getDropdownStyle + }; +} \ No newline at end of file diff --git a/src/panelWebView/styles.css b/src/panelWebView/styles.css index d1203b9a..6ff892f9 100644 --- a/src/panelWebView/styles.css +++ b/src/panelWebView/styles.css @@ -776,6 +776,11 @@ vscode-divider { padding: 0.5rem; } +/* Dropdown */ +.field_dropdown.closed { + display: none; +} + /* Tags */ .article__tags { position: relative;