diff --git a/web/components/custom-attributes/attribute-display/email.tsx b/web/components/custom-attributes/attribute-display/email.tsx index 9a64708a3..88d87f26e 100644 --- a/web/components/custom-attributes/attribute-display/email.tsx +++ b/web/components/custom-attributes/attribute-display/email.tsx @@ -50,10 +50,10 @@ export const CustomEmailAttribute: React.FC = ({ attributeDetails, onChan }); return ( -
+
{!isEditing && (
setIsEditing(true)} > {value && value !== "" ? ( @@ -66,7 +66,11 @@ export const CustomEmailAttribute: React.FC = ({ attributeDetails, onChan
)} {isEditing && ( -
+ = ({ attributeDetails, onCha const extraSettings = attributeDetails.extra_settings; return ( -
+
{!isEditing && ( -
setIsEditing(true)}> +
setIsEditing(true)} + > {value ? ( <> {extraSettings?.representation === "bar" ? ( @@ -91,14 +94,14 @@ export const CustomNumberAttribute: React.FC = ({ attributeDetails, onCha />
) : ( - + {value} )} ) : (
setIsEditing(true)} > {value ?? "Empty"} @@ -107,7 +110,11 @@ export const CustomNumberAttribute: React.FC = ({ attributeDetails, onCha
)} {isEditing && ( - + = ({ const router = useRouter(); const { workspaceSlug } = router.query; - const [isOpen, setIsOpen] = useState(false); const [query, setQuery] = useState(""); - const dropdownButtonRef = useRef(null); - const dropdownOptionsRef = useRef(null); + const [referenceElement, setReferenceElement] = useState(null); + const [popperElement, setPopperElement] = useState(null); + + const { styles, attributes } = usePopper(referenceElement, popperElement, { + placement: "bottom-start", + }); const { data: cycles } = useSWR( workspaceSlug && projectId && attributeDetails.unit === "cycle" @@ -90,8 +90,6 @@ export const CustomRelationAttribute: React.FC = ({ option.query.toLowerCase().includes(query.toLowerCase()) ); - useDynamicDropdownPosition(isOpen, () => setIsOpen(false), dropdownButtonRef, dropdownOptionsRef); - return ( = ({ onChange={(val) => onChange(val)} className="flex-shrink-0 text-left flex items-center" > - {({ open }: { open: boolean }) => ( - <> - - {selectedOption?.label ?? `Select ${attributeDetails.unit}`} - - -
- - setQuery(e.target.value)} - placeholder="Type to search..." - displayValue={(assigned: any) => assigned?.name} - /> -
-
- {options ? ( - options.length > 0 ? ( - options.map((option) => ( - - {option.label} - - )) - ) : ( -

- No {attributeDetails.unit}s found -

- ) + + + + +
+
+ + setQuery(e.target.value)} + placeholder="Type to search..." + displayValue={(assigned: any) => assigned?.name} + /> +
+
+ {options ? ( + options.length > 0 ? ( + options.map((option) => ( + + {option.label} + + )) ) : ( -

Loading...

- )} -
- - - )} +

+ No {attributeDetails.unit}s found +

+ ) + ) : ( +

Loading...

+ )} +
+
+
); }; diff --git a/web/components/custom-attributes/attribute-display/select.tsx b/web/components/custom-attributes/attribute-display/select.tsx index f066b7f32..218756a93 100644 --- a/web/components/custom-attributes/attribute-display/select.tsx +++ b/web/components/custom-attributes/attribute-display/select.tsx @@ -1,9 +1,7 @@ -import React, { useRef, useState } from "react"; - -// headless ui +import React, { useState } from "react"; +import { usePopper } from "react-popper"; import { Combobox } from "@headlessui/react"; -// hooks -import useDynamicDropdownPosition from "hooks/use-dynamic-dropdown"; + // icons import { Check, Search, XIcon } from "lucide-react"; // types @@ -24,11 +22,14 @@ type Props = { export const CustomSelectAttribute: React.FC = (props) => { const { attributeDetails, className = "", multiple = false, onChange, value } = props; - const [isOpen, setIsOpen] = useState(false); const [query, setQuery] = useState(""); - const dropdownButtonRef = useRef(null); - const dropdownOptionsRef = useRef(null); + const [referenceElement, setReferenceElement] = useState(null); + const [popperElement, setPopperElement] = useState(null); + + const { styles, attributes } = usePopper(referenceElement, popperElement, { + placement: "bottom-start", + }); const options = attributeDetails.children.filter((option) => option.display_name.toLowerCase().includes(query.toLowerCase()) @@ -39,137 +40,127 @@ export const CustomSelectAttribute: React.FC = (props) => { value, }; - useDynamicDropdownPosition(isOpen, () => setIsOpen(false), dropdownButtonRef, dropdownOptionsRef); - if (multiple) comboboxProps.multiple = true; return ( - {({ open }: { open: boolean }) => { - if (open) { - if (!isOpen) setIsOpen(true); - } else if (isOpen) setIsOpen(false); + + + + +
+
+ + setQuery(e.target.value)} + placeholder="Type to search..." + displayValue={(assigned: any) => assigned?.name} + /> +
+
+ {(options ?? []).map((option) => ( + + `flex items-center justify-between gap-1 cursor-pointer select-none truncate rounded px-1 py-1.5 w-full ${ + active ? "bg-custom-background-80" : "" + }` + } + > + {({ selected }) => ( + <> + + {option.display_name} + + {selected && } + + )} + + ))} +
+
+
); }; diff --git a/web/components/custom-attributes/attribute-display/text.tsx b/web/components/custom-attributes/attribute-display/text.tsx index f80144b7b..ec8c9545a 100644 --- a/web/components/custom-attributes/attribute-display/text.tsx +++ b/web/components/custom-attributes/attribute-display/text.tsx @@ -56,17 +56,22 @@ export const CustomTextAttribute: React.FC +
{!isEditing && ( -
setIsEditing(true)} + className="cursor-pointer text-xs truncate bg-custom-background-80 px-2.5 py-0.5 rounded" > {value && value !== "" ? value : "Empty"} -
+ )} {isEditing && ( - + }); return ( -
+
{!isEditing && (
setIsEditing(true)} > {value && value !== "" ? ( @@ -70,7 +70,11 @@ export const CustomUrlAttribute: React.FC
)} {isEditing && ( - + = ({ onClick }) => { - const [isOpen, setIsOpen] = useState(false); + const [referenceElement, setReferenceElement] = useState(null); + const [popperElement, setPopperElement] = useState(null); - const buttonRef = useRef(null); - const optionsRef = useRef(null); - - useDynamicDropdownPosition(isOpen, () => setIsOpen(false), buttonRef, optionsRef); + const { styles, attributes } = usePopper(referenceElement, popperElement, { + placement: "top", + }); return ( - {({ open }: { open: boolean }) => { - if (open) { - if (!isOpen) setIsOpen(true); - } else if (isOpen) setIsOpen(false); + + + + +
+ {Object.keys(CUSTOM_ATTRIBUTES_LIST).map((type) => { + const typeMetaData = CUSTOM_ATTRIBUTES_LIST[type as TCustomAttributeTypes]; - return ( - <> - - - Add Attribute - -
- onClick(type as TCustomAttributeTypes)} + className="flex items-center gap-1 cursor-pointer select-none truncate rounded px-1 py-1.5 hover:bg-custom-background-80 w-full" > - {Object.keys(CUSTOM_ATTRIBUTES_LIST).map((type) => { - const typeMetaData = CUSTOM_ATTRIBUTES_LIST[type as TCustomAttributeTypes]; - - return ( - onClick(type as TCustomAttributeTypes)} - className="flex items-center gap-1 cursor-pointer select-none truncate rounded px-1 py-1.5 hover:bg-custom-background-80 w-full" - > - - {typeMetaData.label} - - ); - })} - -
- - ); - }} + + {typeMetaData.label} + + ); + })} +
+
); }; diff --git a/web/components/issues/peek-overview/layout.tsx b/web/components/issues/peek-overview/layout.tsx index bd40fb0a8..57bcb904c 100644 --- a/web/components/issues/peek-overview/layout.tsx +++ b/web/components/issues/peek-overview/layout.tsx @@ -102,17 +102,17 @@ export const IssuePeekOverview: React.FC = observer( /> - - -
+
+ + setDeleteIssueModal(true)} @@ -123,9 +123,9 @@ export const IssuePeekOverview: React.FC = observer( setMode={(mode) => setPeekOverviewMode(mode)} workspaceSlug={workspaceSlug} /> -
- - + + +
diff --git a/web/package.json b/web/package.json index 93fc9366e..942116ce5 100644 --- a/web/package.json +++ b/web/package.json @@ -25,6 +25,7 @@ "@nivo/line": "0.80.0", "@nivo/pie": "0.80.0", "@nivo/scatterplot": "0.80.0", + "@popperjs/core": "^2.11.8", "@sentry/nextjs": "^7.36.0", "@tiptap/extension-code-block-lowlight": "^2.0.4", "@tiptap/extension-color": "^2.0.4", @@ -72,6 +73,7 @@ "react-hook-form": "^7.38.0", "react-markdown": "^8.0.7", "react-moveable": "^0.54.1", + "react-popper": "^2.3.0", "sharp": "^0.32.1", "sonner": "^0.6.2", "swr": "^2.1.3", diff --git a/yarn.lock b/yarn.lock index 9d18f4cee..4b7d3f07c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6628,20 +6628,13 @@ prosemirror-menu@^1.2.1: prosemirror-history "^1.0.0" prosemirror-state "^1.0.0" -prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.18.1, prosemirror-model@^1.8.1: +prosemirror-model@1.18.1, prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.18.1, prosemirror-model@^1.19.0, prosemirror-model@^1.8.1: version "1.18.1" resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.18.1.tgz#1d5d6b6de7b983ee67a479dc607165fdef3935bd" integrity sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw== dependencies: orderedmap "^2.0.0" -prosemirror-model@^1.19.0: - version "1.19.3" - resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.19.3.tgz#f0d55285487fefd962d0ac695f716f4ec6705006" - integrity sha512-tgSnwN7BS7/UM0sSARcW+IQryx2vODKX4MI7xpqY2X+iaepJdKBPc7I4aACIsDV/LTaTjt12Z56MhDr9LsyuZQ== - dependencies: - orderedmap "^2.0.0" - prosemirror-schema-basic@^1.2.0: version "1.2.2" resolved "https://registry.yarnpkg.com/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.2.tgz#6695f5175e4628aab179bf62e5568628b9cfe6c7"