PromucFlow_constructor/app/client/src/widgets/useDropdown.tsx
Ilia d6f249b42d
chore: add blank line eslint rule (#36369)
## Description
Added ESLint rule to force blank lines between statements. 


Fixes #`Issue Number`  
_or_  
Fixes `Issue URL`
> [!WARNING]  
> _If no issue exists, please create an issue first, and check with the
maintainers if the issue is valid._

## Automation

/ok-to-test tags="@tag.All"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!CAUTION]
> 🔴 🔴 🔴 Some tests have failed.
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/10924926728>
> Commit: 34f57714a1575ee04e94e03cbcaf95e57a96c86c
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=10924926728&attempt=1&selectiontype=test&testsstatus=failed&specsstatus=fail"
target="_blank">Cypress dashboard</a>.
> Tags: @tag.All
> Spec: 
> The following are new failures, please fix them before merging the PR:
<ol>
> <li>cypress/e2e/Regression/ClientSide/Anvil/AnvilModal_spec.ts
>
<li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilButtonWidgetSnapshot_spec.ts
>
<li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilCheckboxGroupWidgetSnapshot_spec.ts
>
<li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilCurrencyInputWidgetSnapshot_spec.ts
>
<li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilIconButtonWidgetSnapshot_spec.ts
>
<li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilInlineButtonWidgetSnapshot_spec.ts
>
<li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilInputWidgetSnapshot_spec.ts
>
<li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilParagraphWidgetSnapshot_spec.ts
>
<li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilPhoneInputWidgetSnapshot_spec.ts
>
<li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilStatsWidgetSnapshot_spec.ts
>
<li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilSwitchGroupWidgetSnapshot_spec.ts
>
<li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilSwitchWidgetSnapshot_spec.ts
>
<li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilTableWidgetSnapshot_spec.ts
>
<li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilToolbarButtonWidgetSnapshot_spec.ts
>
<li>cypress/e2e/Regression/ClientSide/Anvil/Widgets/AnvilZoneSectionWidgetSnapshot_spec.ts</ol>
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/identified-flaky-tests-65890b3c81d7400d08fa9ee3?branch=master"
target="_blank">List of identified flaky tests</a>.
> <hr>Wed, 18 Sep 2024 16:33:36 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No

---------

Co-authored-by: Valera Melnikov <valera@appsmith.com>
2024-09-18 19:35:28 +03:00

115 lines
3.1 KiB
TypeScript

import React, { useCallback, useEffect, useRef, useState } from "react";
import { getMainCanvas } from "./WidgetUtils";
import styled from "styled-components";
import type { BaseSelectRef } from "rc-select";
import type { RenderMode } from "constants/WidgetConstants";
import { RenderModes } from "constants/WidgetConstants";
const BackDropContainer = styled.div`
position: fixed;
width: 100vw;
height: 100vh;
background: transparent;
top: 0;
left: 0;
display: none;
`;
interface useDropdownProps {
inputRef: React.RefObject<HTMLInputElement>;
renderMode?: RenderMode;
onDropdownOpen?: () => void;
onDropdownClose?: () => void;
}
const FOCUS_TIMEOUT = 500;
// TODO: Refactor More functionalities in MultiSelect, MultiTreeSelect and TreeSelect Components
const useDropdown = ({
inputRef,
onDropdownClose,
onDropdownOpen,
renderMode,
}: useDropdownProps) => {
// This is to make the dropdown controlled
const [isOpen, setIsOpen] = useState(false);
const popupContainer = useRef<HTMLElement>(getMainCanvas());
const selectRef = useRef<BaseSelectRef | null>(null);
useEffect(() => {
if (!popupContainer.current) {
popupContainer.current = getMainCanvas();
}
}, []);
const onKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
// Backspace would simultaneously remove an option, so it should only be used within the search input
if (event.key === "Backspace") {
event.stopPropagation();
}
};
// Avoid scrolls when Popup is opened
function BackDrop() {
return <BackDropContainer onClick={closeBackDrop} />;
}
// Get PopupContainer on main Canvas
const getPopupContainer = useCallback(() => popupContainer.current, []);
const handleOnDropdownOpen = useCallback(() => {
if (!isOpen && onDropdownOpen) {
onDropdownOpen();
}
}, [onDropdownOpen, isOpen]);
const handleOnDropdownClose = useCallback(() => {
if (isOpen && onDropdownClose) {
onDropdownClose();
}
}, [onDropdownClose, isOpen]);
// When Dropdown is opened disable scrolling within the app except the list of options
const onOpen = useCallback(
(open: boolean) => {
setIsOpen(open);
if (open) {
handleOnDropdownOpen();
setTimeout(() => inputRef.current?.focus(), FOCUS_TIMEOUT);
// for more context, the Element we attach to in view mode doesn't have an overflow style, so this only applies to edit mode.
if (popupContainer.current && renderMode === RenderModes.CANVAS) {
popupContainer.current.style.overflowY = "hidden";
}
} else {
handleOnDropdownClose();
if (popupContainer.current && renderMode === RenderModes.CANVAS) {
popupContainer.current.style.overflowY = "auto";
}
selectRef.current?.blur();
}
},
[renderMode, handleOnDropdownOpen, handleOnDropdownOpen],
);
const closeBackDrop = useCallback(() => {
if (selectRef.current) {
selectRef.current.blur();
onOpen(false);
}
}, [onOpen]);
return {
BackDrop,
getPopupContainer,
onOpen,
isOpen,
selectRef,
onKeyDown,
};
};
export default useDropdown;