mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
chore: integrate popper js (#2398)
* chore: react-popper-js added * chore: integrate popper js in issue properties dropdown * chore: integrate popper js in custom menu component * chore: integrate popper js in custom select component * chore: integrate popper js in custom search select component * chore: popper js placement type added * chore: popper js placement type added
This commit is contained in:
parent
d88eb09fad
commit
58ea4d6ec9
@ -74,7 +74,6 @@ export const AutoArchiveAutomation: React.FC<Props> = ({
|
|||||||
handleChange({ archive_in: val });
|
handleChange({ archive_in: val });
|
||||||
}}
|
}}
|
||||||
input
|
input
|
||||||
verticalPosition="bottom"
|
|
||||||
width="w-full"
|
width="w-full"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
|
@ -100,7 +100,6 @@ export const ThemeSwitch: React.FC<Props> = observer(
|
|||||||
}}
|
}}
|
||||||
input
|
input
|
||||||
width="w-full"
|
width="w-full"
|
||||||
position="right"
|
|
||||||
>
|
>
|
||||||
{THEMES_OBJ.map(({ value, label, type, icon }) => (
|
{THEMES_OBJ.map(({ value, label, type, icon }) => (
|
||||||
<CustomSelect.Option key={value} value={{ value, type }}>
|
<CustomSelect.Option key={value} value={{ value, type }}>
|
||||||
|
@ -255,15 +255,11 @@ export const SingleBoard: React.FC<Props> = (props) => {
|
|||||||
!isDraftIssuesPage && (
|
!isDraftIssuesPage && (
|
||||||
<CustomMenu
|
<CustomMenu
|
||||||
customButton={
|
customButton={
|
||||||
<button
|
<div className="flex items-center gap-2 font-medium text-custom-primary outline-none whitespace-nowrap">
|
||||||
type="button"
|
|
||||||
className="flex items-center gap-2 font-medium text-custom-primary outline-none whitespace-nowrap"
|
|
||||||
>
|
|
||||||
<PlusIcon className="h-4 w-4" />
|
<PlusIcon className="h-4 w-4" />
|
||||||
Add Issue
|
Add Issue
|
||||||
</button>
|
</div>
|
||||||
}
|
}
|
||||||
position="left"
|
|
||||||
noBorder
|
noBorder
|
||||||
>
|
>
|
||||||
<CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
|
@ -369,12 +369,12 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
{type && !isNotAllowed && (
|
{type && !isNotAllowed && (
|
||||||
<CustomMenu
|
<CustomMenu
|
||||||
customButton={
|
customButton={
|
||||||
<button
|
<div
|
||||||
className="flex w-full cursor-pointer items-center justify-between gap-1 rounded p-1 text-left text-xs duration-300 hover:bg-custom-background-80"
|
className="flex w-full cursor-pointer items-center justify-between gap-1 rounded p-1 text-left text-xs duration-300 hover:bg-custom-background-80"
|
||||||
onClick={() => setIsMenuActive(!isMenuActive)}
|
onClick={() => setIsMenuActive(!isMenuActive)}
|
||||||
>
|
>
|
||||||
<EllipsisHorizontalIcon className="h-4 w-4" />
|
<EllipsisHorizontalIcon className="h-4 w-4" />
|
||||||
</button>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
|
@ -272,7 +272,6 @@ export const SingleList: React.FC<Props> = (props) => {
|
|||||||
<PlusIcon className="h-4 w-4" />
|
<PlusIcon className="h-4 w-4" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
position="right"
|
|
||||||
noBorder
|
noBorder
|
||||||
>
|
>
|
||||||
<CustomMenu.MenuItem onClick={() => setIsCreateIssueFormOpen(true)}>
|
<CustomMenu.MenuItem onClick={() => setIsCreateIssueFormOpen(true)}>
|
||||||
|
@ -255,7 +255,6 @@ export const SpreadsheetView: React.FC<Props> = ({
|
|||||||
<CustomMenu
|
<CustomMenu
|
||||||
customButtonClassName="!w-full"
|
customButtonClassName="!w-full"
|
||||||
className="!w-full"
|
className="!w-full"
|
||||||
position="left"
|
|
||||||
customButton={
|
customButton={
|
||||||
<div
|
<div
|
||||||
className={`relative group flex items-center justify-between gap-1.5 cursor-pointer text-sm text-custom-text-200 hover:text-custom-text-100 w-full py-3 px-2 ${
|
className={`relative group flex items-center justify-between gap-1.5 cursor-pointer text-sm text-custom-text-200 hover:text-custom-text-100 w-full py-3 px-2 ${
|
||||||
@ -641,16 +640,11 @@ export const SpreadsheetView: React.FC<Props> = ({
|
|||||||
<CustomMenu
|
<CustomMenu
|
||||||
className="sticky left-0 z-10"
|
className="sticky left-0 z-10"
|
||||||
customButton={
|
customButton={
|
||||||
<button
|
<div className="flex gap-1.5 items-center text-custom-primary-100 pl-4 py-2.5 text-sm sticky left-0 z-[1] border-custom-border-200 w-full">
|
||||||
className="flex gap-1.5 items-center text-custom-primary-100 pl-4 py-2.5 text-sm sticky left-0 z-[1] border-custom-border-200 w-full"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<PlusIcon className="h-4 w-4" />
|
<PlusIcon className="h-4 w-4" />
|
||||||
New Issue
|
New Issue
|
||||||
</button>
|
</div>
|
||||||
}
|
}
|
||||||
position="left"
|
|
||||||
verticalPosition="top"
|
|
||||||
optionsClassName="left-5 !w-36"
|
optionsClassName="left-5 !w-36"
|
||||||
noBorder
|
noBorder
|
||||||
>
|
>
|
||||||
|
@ -92,7 +92,6 @@ export const SelectRepository: React.FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
position="right"
|
|
||||||
optionsClassName="w-full"
|
optionsClassName="w-full"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -143,7 +143,6 @@ export const JiraGetImportDetail: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
verticalPosition="top"
|
|
||||||
>
|
>
|
||||||
{projects && projects.length > 0 ? (
|
{projects && projects.length > 0 ? (
|
||||||
projects.map((project) => (
|
projects.map((project) => (
|
||||||
|
@ -158,7 +158,7 @@ export const IssueMainContent: React.FC<Props> = ({
|
|||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<CustomMenu position="left" ellipsis optionsClassName="px-1.5">
|
<CustomMenu ellipsis optionsClassName="px-1.5">
|
||||||
{siblingIssuesList ? (
|
{siblingIssuesList ? (
|
||||||
siblingIssuesList.length > 0 ? (
|
siblingIssuesList.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
|
@ -93,7 +93,6 @@ export const PeekOverviewHeader: React.FC<Props> = ({
|
|||||||
<Icon iconName={peekModes.find((m) => m.key === mode)?.icon ?? ""} />
|
<Icon iconName={peekModes.find((m) => m.key === mode)?.icon ?? ""} />
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
position="left"
|
|
||||||
>
|
>
|
||||||
{peekModes.map((mode) => (
|
{peekModes.map((mode) => (
|
||||||
<CustomSelect.Option key={mode.key} value={mode.key}>
|
<CustomSelect.Option key={mode.key} value={mode.key}>
|
||||||
|
@ -33,7 +33,6 @@ export const IssueEstimateSelect: React.FC<Props> = ({ value, onChange }) => {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
position="right"
|
|
||||||
width="w-full min-w-[8rem]"
|
width="w-full min-w-[8rem]"
|
||||||
noChevron
|
noChevron
|
||||||
>
|
>
|
||||||
|
@ -91,7 +91,6 @@ export const SidebarCycleSelect: React.FC<Props> = ({
|
|||||||
: handleCycleChange(incompleteCycles?.find((c) => c.id === value) as ICycle);
|
: handleCycleChange(incompleteCycles?.find((c) => c.id === value) as ICycle);
|
||||||
}}
|
}}
|
||||||
width="w-full"
|
width="w-full"
|
||||||
position="right"
|
|
||||||
maxHeight="rg"
|
maxHeight="rg"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
|
@ -20,17 +20,14 @@ export const SidebarEstimateSelect: React.FC<Props> = ({ value, onChange, disabl
|
|||||||
<CustomSelect
|
<CustomSelect
|
||||||
value={value}
|
value={value}
|
||||||
customButton={
|
customButton={
|
||||||
<button
|
<div className="flex items-center gap-1.5 !text-sm bg-custom-background-80 rounded px-2.5 py-0.5">
|
||||||
type="button"
|
|
||||||
className="flex items-center gap-1.5 !text-sm bg-custom-background-80 rounded px-2.5 py-0.5"
|
|
||||||
>
|
|
||||||
<PlayIcon
|
<PlayIcon
|
||||||
className={`h-4 w-4 -rotate-90 ${
|
className={`h-4 w-4 -rotate-90 ${
|
||||||
value !== null ? "text-custom-text-100" : "text-custom-text-200"
|
value !== null ? "text-custom-text-100" : "text-custom-text-200"
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
{estimatePoints?.find((e) => e.key === value)?.value ?? "No estimate"}
|
{estimatePoints?.find((e) => e.key === value)?.value ?? "No estimate"}
|
||||||
</button>
|
</div>
|
||||||
}
|
}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
@ -87,7 +87,6 @@ export const SidebarModuleSelect: React.FC<Props> = ({
|
|||||||
: handleModuleChange(modules?.find((m) => m.id === value) as IModule);
|
: handleModuleChange(modules?.find((m) => m.id === value) as IModule);
|
||||||
}}
|
}}
|
||||||
width="w-full"
|
width="w-full"
|
||||||
position="right"
|
|
||||||
maxHeight="rg"
|
maxHeight="rg"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
|
@ -18,8 +18,7 @@ type Props = {
|
|||||||
export const SidebarPrioritySelect: React.FC<Props> = ({ value, onChange, disabled = false }) => (
|
export const SidebarPrioritySelect: React.FC<Props> = ({ value, onChange, disabled = false }) => (
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
customButton={
|
customButton={
|
||||||
<button
|
<div
|
||||||
type="button"
|
|
||||||
className={`flex items-center gap-1.5 text-left text-xs capitalize rounded px-2.5 py-0.5 ${
|
className={`flex items-center gap-1.5 text-left text-xs capitalize rounded px-2.5 py-0.5 ${
|
||||||
value === "urgent"
|
value === "urgent"
|
||||||
? "border-red-500/20 bg-red-500/20 text-red-500"
|
? "border-red-500/20 bg-red-500/20 text-red-500"
|
||||||
@ -36,7 +35,7 @@ export const SidebarPrioritySelect: React.FC<Props> = ({ value, onChange, disabl
|
|||||||
<PriorityIcon priority={value} className="!text-sm" />
|
<PriorityIcon priority={value} className="!text-sm" />
|
||||||
</span>
|
</span>
|
||||||
<span>{value ?? "None"}</span>
|
<span>{value ?? "None"}</span>
|
||||||
</button>
|
</div>
|
||||||
}
|
}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
@ -39,7 +39,7 @@ export const SidebarStateSelect: React.FC<Props> = ({ value, onChange, disabled
|
|||||||
return (
|
return (
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
customButton={
|
customButton={
|
||||||
<button type="button" className="bg-custom-background-80 text-xs rounded px-2.5 py-0.5">
|
<div className="bg-custom-background-80 text-xs rounded px-2.5 py-0.5">
|
||||||
{selectedState ? (
|
{selectedState ? (
|
||||||
<div className="flex items-center gap-1.5 text-left text-custom-text-100">
|
<div className="flex items-center gap-1.5 text-left text-custom-text-100">
|
||||||
<StateGroupIcon stateGroup={selectedState.group} color={selectedState.color} />
|
<StateGroupIcon stateGroup={selectedState.group} color={selectedState.color} />
|
||||||
@ -53,12 +53,11 @@ export const SidebarStateSelect: React.FC<Props> = ({ value, onChange, disabled
|
|||||||
) : (
|
) : (
|
||||||
"None"
|
"None"
|
||||||
)}
|
)}
|
||||||
</button>
|
</div>
|
||||||
}
|
}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
optionsClassName="w-min"
|
optionsClassName="w-min"
|
||||||
position="left"
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
{states ? (
|
{states ? (
|
||||||
|
@ -247,7 +247,6 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = ({ parentIssue, user }) =
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
buttonClassName="whitespace-nowrap"
|
buttonClassName="whitespace-nowrap"
|
||||||
position="left"
|
|
||||||
noBorder
|
noBorder
|
||||||
noChevron
|
noChevron
|
||||||
>
|
>
|
||||||
@ -283,7 +282,6 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = ({ parentIssue, user }) =
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
buttonClassName="whitespace-nowrap"
|
buttonClassName="whitespace-nowrap"
|
||||||
position="left"
|
|
||||||
noBorder
|
noBorder
|
||||||
noChevron
|
noChevron
|
||||||
>
|
>
|
||||||
|
@ -113,10 +113,8 @@ export const ViewAssigneeSelect: React.FC<Props> = ({
|
|||||||
{...(customButton ? { customButton: assigneeLabel } : { label: assigneeLabel })}
|
{...(customButton ? { customButton: assigneeLabel } : { label: assigneeLabel })}
|
||||||
multiple
|
multiple
|
||||||
noChevron
|
noChevron
|
||||||
position={position}
|
|
||||||
disabled={isNotAllowed}
|
disabled={isNotAllowed}
|
||||||
onOpen={() => setFetchAssignees(true)}
|
onOpen={() => setFetchAssignees(true)}
|
||||||
selfPositioned={selfPositioned}
|
|
||||||
width="w-full min-w-[12rem]"
|
width="w-full min-w-[12rem]"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -74,8 +74,6 @@ export const ViewEstimateSelect: React.FC<Props> = ({
|
|||||||
maxHeight="md"
|
maxHeight="md"
|
||||||
noChevron
|
noChevron
|
||||||
disabled={isNotAllowed}
|
disabled={isNotAllowed}
|
||||||
position={position}
|
|
||||||
selfPositioned={selfPositioned}
|
|
||||||
width="w-full min-w-[8rem]"
|
width="w-full min-w-[8rem]"
|
||||||
>
|
>
|
||||||
<CustomSelect.Option value={null}>
|
<CustomSelect.Option value={null}>
|
||||||
|
@ -146,9 +146,7 @@ export const ViewLabelSelect: React.FC<Props> = ({
|
|||||||
{...(customButton ? { customButton: labelsLabel } : { label: labelsLabel })}
|
{...(customButton ? { customButton: labelsLabel } : { label: labelsLabel })}
|
||||||
multiple
|
multiple
|
||||||
noChevron
|
noChevron
|
||||||
position={position}
|
|
||||||
disabled={isNotAllowed}
|
disabled={isNotAllowed}
|
||||||
selfPositioned={selfPositioned}
|
|
||||||
footerOption={footerOption}
|
footerOption={footerOption}
|
||||||
width="w-full min-w-[12rem]"
|
width="w-full min-w-[12rem]"
|
||||||
/>
|
/>
|
||||||
|
@ -59,8 +59,7 @@ export const ViewPrioritySelect: React.FC<Props> = ({
|
|||||||
}}
|
}}
|
||||||
maxHeight="md"
|
maxHeight="md"
|
||||||
customButton={
|
customButton={
|
||||||
<button
|
<div
|
||||||
type="button"
|
|
||||||
className={`grid place-items-center rounded ${
|
className={`grid place-items-center rounded ${
|
||||||
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"
|
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"
|
||||||
} ${noBorder ? "" : "h-6 w-6 border shadow-sm"} ${
|
} ${noBorder ? "" : "h-6 w-6 border shadow-sm"} ${
|
||||||
@ -94,12 +93,10 @@ export const ViewPrioritySelect: React.FC<Props> = ({
|
|||||||
{noBorder ? capitalizeFirstLetter(issue.priority ?? "None") : ""}
|
{noBorder ? capitalizeFirstLetter(issue.priority ?? "None") : ""}
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</button>
|
</div>
|
||||||
}
|
}
|
||||||
noChevron
|
noChevron
|
||||||
disabled={isNotAllowed}
|
disabled={isNotAllowed}
|
||||||
position={position}
|
|
||||||
selfPositioned={selfPositioned}
|
|
||||||
>
|
>
|
||||||
{PRIORITIES?.map((priority) => (
|
{PRIORITIES?.map((priority) => (
|
||||||
<CustomSelect.Option key={priority} value={priority} className="capitalize">
|
<CustomSelect.Option key={priority} value={priority} className="capitalize">
|
||||||
|
@ -65,7 +65,6 @@ export const SidebarLeadSelect: React.FC<Props> = ({ value, onChange }) => {
|
|||||||
}
|
}
|
||||||
options={options}
|
options={options}
|
||||||
maxHeight="md"
|
maxHeight="md"
|
||||||
position="right"
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -64,7 +64,6 @@ export const SidebarMembersSelect: React.FC<Props> = ({ value, onChange }) => {
|
|||||||
options={options}
|
options={options}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
maxHeight="md"
|
maxHeight="md"
|
||||||
position="right"
|
|
||||||
multiple
|
multiple
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -214,12 +214,9 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}}
|
}}
|
||||||
customButton={
|
customButton={
|
||||||
<button
|
<div className="text-sm flex w-full items-center gap-x-2 bg-custom-background-80 hover:bg-custom-background-100 p-0.5 rounded">
|
||||||
type="button"
|
|
||||||
className="text-sm flex w-full items-center gap-x-2 bg-custom-background-80 hover:bg-custom-background-100 p-0.5 rounded"
|
|
||||||
>
|
|
||||||
<Icon iconName="schedule" className="h-5 w-5 text-custom-text-300" />
|
<Icon iconName="schedule" className="h-5 w-5 text-custom-text-300" />
|
||||||
</button>
|
</div>
|
||||||
}
|
}
|
||||||
optionsClassName="!z-20"
|
optionsClassName="!z-20"
|
||||||
>
|
>
|
||||||
|
@ -167,7 +167,6 @@ export const UserDetails: React.FC<Props> = ({ user }) => {
|
|||||||
}
|
}
|
||||||
input
|
input
|
||||||
width="w-full"
|
width="w-full"
|
||||||
verticalPosition="top"
|
|
||||||
>
|
>
|
||||||
{USER_ROLES.map((item) => (
|
{USER_ROLES.map((item) => (
|
||||||
<CustomSelect.Option key={item.value} value={item.value}>
|
<CustomSelect.Option key={item.value} value={item.value}>
|
||||||
@ -197,7 +196,6 @@ export const UserDetails: React.FC<Props> = ({ user }) => {
|
|||||||
}
|
}
|
||||||
options={timeZoneOptions}
|
options={timeZoneOptions}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
verticalPosition="top"
|
|
||||||
optionsClassName="w-full"
|
optionsClassName="w-full"
|
||||||
input
|
input
|
||||||
/>
|
/>
|
||||||
|
@ -384,12 +384,12 @@ export const SinglePageBlock: React.FC<Props> = ({
|
|||||||
</button>
|
</button>
|
||||||
<CustomMenu
|
<CustomMenu
|
||||||
customButton={
|
customButton={
|
||||||
<button
|
<div
|
||||||
className="flex w-full cursor-pointer items-center justify-between gap-1 rounded px-2.5 py-1 text-left text-xs duration-300 hover:bg-custom-background-90"
|
className="flex w-full cursor-pointer items-center justify-between gap-1 rounded px-2.5 py-1 text-left text-xs duration-300 hover:bg-custom-background-90"
|
||||||
onClick={() => setIsMenuActive(!isMenuActive)}
|
onClick={() => setIsMenuActive(!isMenuActive)}
|
||||||
>
|
>
|
||||||
<BoltIcon className="h-4.5 w-3.5" />
|
<BoltIcon className="h-4.5 w-3.5" />
|
||||||
</button>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{block.issue ? (
|
{block.issue ? (
|
||||||
|
@ -418,7 +418,6 @@ export const CreateProjectModal: React.FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
verticalPosition="top"
|
|
||||||
noChevron
|
noChevron
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
// react-popper
|
||||||
|
import { usePopper } from "react-popper";
|
||||||
// services
|
// services
|
||||||
import issuesService from "services/issues.service";
|
import issuesService from "services/issues.service";
|
||||||
// hooks
|
|
||||||
import useDynamicDropdownPosition from "hooks/use-dynamic-dropdown";
|
|
||||||
// headless ui
|
// headless ui
|
||||||
import { Combobox } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
// component
|
// component
|
||||||
@ -19,6 +19,7 @@ import { PlusIcon } from "lucide-react";
|
|||||||
// types
|
// types
|
||||||
import { Tooltip } from "components/ui";
|
import { Tooltip } from "components/ui";
|
||||||
import { ICurrentUserResponse, IIssueLabels } from "types";
|
import { ICurrentUserResponse, IIssueLabels } from "types";
|
||||||
|
import { Placement } from "@popperjs/core";
|
||||||
// constants
|
// constants
|
||||||
import { PROJECT_ISSUE_LABELS } from "constants/fetch-keys";
|
import { PROJECT_ISSUE_LABELS } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ type Props = {
|
|||||||
buttonClassName?: string;
|
buttonClassName?: string;
|
||||||
optionsClassName?: string;
|
optionsClassName?: string;
|
||||||
maxRender?: number;
|
maxRender?: number;
|
||||||
|
placement?: Placement;
|
||||||
hideDropdownArrow?: boolean;
|
hideDropdownArrow?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
user: ICurrentUserResponse | undefined;
|
user: ICurrentUserResponse | undefined;
|
||||||
@ -45,21 +47,25 @@ export const LabelSelect: React.FC<Props> = ({
|
|||||||
buttonClassName = "",
|
buttonClassName = "",
|
||||||
optionsClassName = "",
|
optionsClassName = "",
|
||||||
maxRender = 2,
|
maxRender = 2,
|
||||||
|
placement,
|
||||||
hideDropdownArrow = false,
|
hideDropdownArrow = false,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
user,
|
user,
|
||||||
}) => {
|
}) => {
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
const [fetchStates, setFetchStates] = useState(false);
|
const [fetchStates, setFetchStates] = useState(false);
|
||||||
|
|
||||||
|
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
||||||
|
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
const [labelModal, setLabelModal] = useState(false);
|
const [labelModal, setLabelModal] = useState(false);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
const dropdownBtn = useRef<any>(null);
|
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||||
const dropdownOptions = useRef<any>(null);
|
placement: placement ?? "bottom-start",
|
||||||
|
});
|
||||||
|
|
||||||
const { data: issueLabels } = useSWR<IIssueLabels[]>(
|
const { data: issueLabels } = useSWR<IIssueLabels[]>(
|
||||||
projectId && fetchStates ? PROJECT_ISSUE_LABELS(projectId) : null,
|
projectId && fetchStates ? PROJECT_ISSUE_LABELS(projectId) : null,
|
||||||
@ -131,8 +137,6 @@ export const LabelSelect: React.FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
useDynamicDropdownPosition(isOpen, () => setIsOpen(false), dropdownBtn, dropdownOptions);
|
|
||||||
|
|
||||||
const footerOption = (
|
const footerOption = (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -165,33 +169,35 @@ export const LabelSelect: React.FC<Props> = ({
|
|||||||
multiple
|
multiple
|
||||||
>
|
>
|
||||||
{({ open }: { open: boolean }) => {
|
{({ open }: { open: boolean }) => {
|
||||||
if (open) {
|
if (open) setFetchStates(true);
|
||||||
if (!isOpen) setIsOpen(true);
|
|
||||||
setFetchStates(true);
|
|
||||||
} else if (isOpen) setIsOpen(false);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Combobox.Button
|
<Combobox.Button as={React.Fragment}>
|
||||||
ref={dropdownBtn}
|
<button
|
||||||
type="button"
|
ref={setReferenceElement}
|
||||||
className={`flex items-center justify-between gap-1 w-full text-xs ${
|
type="button"
|
||||||
disabled
|
className={`flex items-center justify-between gap-1 w-full text-xs ${
|
||||||
? "cursor-not-allowed text-custom-text-200"
|
disabled
|
||||||
: value.length <= maxRender
|
? "cursor-not-allowed text-custom-text-200"
|
||||||
? "cursor-pointer"
|
: value.length <= maxRender
|
||||||
: "cursor-pointer hover:bg-custom-background-80"
|
? "cursor-pointer"
|
||||||
} ${buttonClassName}`}
|
: "cursor-pointer hover:bg-custom-background-80"
|
||||||
>
|
} ${buttonClassName}`}
|
||||||
{label}
|
>
|
||||||
{!hideDropdownArrow && !disabled && (
|
{label}
|
||||||
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
{!hideDropdownArrow && !disabled && (
|
||||||
)}
|
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
</Combobox.Button>
|
</Combobox.Button>
|
||||||
<div className={`${open ? "fixed z-20 top-0 left-0 h-full w-full cursor-auto" : ""}`}>
|
|
||||||
<Combobox.Options
|
<Combobox.Options>
|
||||||
ref={dropdownOptions}
|
<div
|
||||||
className={`absolute z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-lg focus:outline-none w-48 whitespace-nowrap mt-1 ${optionsClassName}`}
|
className={`z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-custom-shadow-rg focus:outline-none w-48 whitespace-nowrap my-1 ${optionsClassName}`}
|
||||||
|
ref={setPopperElement}
|
||||||
|
style={styles.popper}
|
||||||
|
{...attributes.popper}
|
||||||
>
|
>
|
||||||
<div className="flex w-full items-center justify-start rounded border border-custom-border-200 bg-custom-background-90 px-2">
|
<div className="flex w-full items-center justify-start rounded border border-custom-border-200 bg-custom-background-90 px-2">
|
||||||
<MagnifyingGlassIcon className="h-3.5 w-3.5 text-custom-text-300" />
|
<MagnifyingGlassIcon className="h-3.5 w-3.5 text-custom-text-300" />
|
||||||
@ -234,8 +240,8 @@ export const LabelSelect: React.FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{footerOption}
|
{footerOption}
|
||||||
</Combobox.Options>
|
</div>
|
||||||
</div>
|
</Combobox.Options>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
@ -77,7 +77,6 @@ export const MemberSelect: React.FC<Props> = ({ value, onChange, isDisabled = fa
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
maxHeight="md"
|
maxHeight="md"
|
||||||
position="right"
|
|
||||||
width="w-full"
|
width="w-full"
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
// react-popper
|
||||||
|
import { usePopper } from "react-popper";
|
||||||
// hooks
|
// hooks
|
||||||
import useDynamicDropdownPosition from "hooks/use-dynamic-dropdown";
|
|
||||||
import useProjectMembers from "hooks/use-project-members";
|
import useProjectMembers from "hooks/use-project-members";
|
||||||
import useWorkspaceMembers from "hooks/use-workspace-members";
|
import useWorkspaceMembers from "hooks/use-workspace-members";
|
||||||
// headless ui
|
// headless ui
|
||||||
@ -15,6 +16,7 @@ import { ChevronDownIcon } from "@heroicons/react/20/solid";
|
|||||||
import { CheckIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
import { CheckIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
||||||
// types
|
// types
|
||||||
import { IUser } from "types";
|
import { IUser } from "types";
|
||||||
|
import { Placement } from "@popperjs/core";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
value: string | string[];
|
value: string | string[];
|
||||||
@ -25,6 +27,7 @@ type Props = {
|
|||||||
className?: string;
|
className?: string;
|
||||||
buttonClassName?: string;
|
buttonClassName?: string;
|
||||||
optionsClassName?: string;
|
optionsClassName?: string;
|
||||||
|
placement?: Placement;
|
||||||
hideDropdownArrow?: boolean;
|
hideDropdownArrow?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
@ -38,18 +41,22 @@ export const MembersSelect: React.FC<Props> = ({
|
|||||||
className = "",
|
className = "",
|
||||||
buttonClassName = "",
|
buttonClassName = "",
|
||||||
optionsClassName = "",
|
optionsClassName = "",
|
||||||
|
placement,
|
||||||
hideDropdownArrow = false,
|
hideDropdownArrow = false,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
}) => {
|
}) => {
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
const [fetchStates, setFetchStates] = useState(false);
|
const [fetchStates, setFetchStates] = useState(false);
|
||||||
|
|
||||||
|
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
||||||
|
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
const dropdownBtn = useRef<any>(null);
|
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||||
const dropdownOptions = useRef<any>(null);
|
placement: placement ?? "bottom-start",
|
||||||
|
});
|
||||||
|
|
||||||
const { members } = useProjectMembers(
|
const { members } = useProjectMembers(
|
||||||
workspaceSlug?.toString(),
|
workspaceSlug?.toString(),
|
||||||
@ -105,8 +112,6 @@ export const MembersSelect: React.FC<Props> = ({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
|
||||||
useDynamicDropdownPosition(isOpen, () => setIsOpen(false), dropdownBtn, dropdownOptions);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Combobox
|
<Combobox
|
||||||
as="div"
|
as="div"
|
||||||
@ -117,31 +122,32 @@ export const MembersSelect: React.FC<Props> = ({
|
|||||||
multiple
|
multiple
|
||||||
>
|
>
|
||||||
{({ open }: { open: boolean }) => {
|
{({ open }: { open: boolean }) => {
|
||||||
if (open) {
|
if (open) setFetchStates(true);
|
||||||
if (!isOpen) setIsOpen(true);
|
|
||||||
setFetchStates(true);
|
|
||||||
} else if (isOpen) setIsOpen(false);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Combobox.Button
|
<Combobox.Button as={React.Fragment}>
|
||||||
ref={dropdownBtn}
|
<button
|
||||||
type="button"
|
ref={setReferenceElement}
|
||||||
className={`flex items-center justify-between gap-1 w-full text-xs ${
|
type="button"
|
||||||
disabled
|
className={`flex items-center justify-between gap-1 w-full text-xs ${
|
||||||
? "cursor-not-allowed text-custom-text-200"
|
disabled
|
||||||
: "cursor-pointer hover:bg-custom-background-80"
|
? "cursor-not-allowed text-custom-text-200"
|
||||||
} ${buttonClassName}`}
|
: "cursor-pointer hover:bg-custom-background-80"
|
||||||
>
|
} ${buttonClassName}`}
|
||||||
{label}
|
>
|
||||||
{!hideDropdownArrow && !disabled && (
|
{label}
|
||||||
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
{!hideDropdownArrow && !disabled && (
|
||||||
)}
|
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
</Combobox.Button>
|
</Combobox.Button>
|
||||||
<div className={`${open ? "fixed z-20 top-0 left-0 h-full w-full cursor-auto" : ""}`}>
|
<Combobox.Options>
|
||||||
<Combobox.Options
|
<div
|
||||||
ref={dropdownOptions}
|
className={`z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-custom-shadow-rg focus:outline-none w-48 whitespace-nowrap my-1 ${optionsClassName}`}
|
||||||
className={`absolute z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-lg focus:outline-none w-48 whitespace-nowrap mt-1 ${optionsClassName}`}
|
ref={setPopperElement}
|
||||||
|
style={styles.popper}
|
||||||
|
{...attributes.popper}
|
||||||
>
|
>
|
||||||
<div className="flex w-full items-center justify-start rounded border border-custom-border-200 bg-custom-background-90 px-2">
|
<div className="flex w-full items-center justify-start rounded border border-custom-border-200 bg-custom-background-90 px-2">
|
||||||
<MagnifyingGlassIcon className="h-3.5 w-3.5 text-custom-text-300" />
|
<MagnifyingGlassIcon className="h-3.5 w-3.5 text-custom-text-300" />
|
||||||
@ -183,8 +189,8 @@ export const MembersSelect: React.FC<Props> = ({
|
|||||||
<p className="text-center text-custom-text-200">Loading...</p>
|
<p className="text-center text-custom-text-200">Loading...</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Combobox.Options>
|
</div>
|
||||||
</div>
|
</Combobox.Options>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
// hooks
|
// react-popper
|
||||||
import useDynamicDropdownPosition from "hooks/use-dynamic-dropdown";
|
import { usePopper } from "react-popper";
|
||||||
// headless ui
|
// headless ui
|
||||||
import { Combobox } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
// icons
|
// icons
|
||||||
@ -12,6 +12,7 @@ import { PriorityIcon } from "components/icons";
|
|||||||
import { Tooltip } from "components/ui";
|
import { Tooltip } from "components/ui";
|
||||||
// types
|
// types
|
||||||
import { TIssuePriorities } from "types";
|
import { TIssuePriorities } from "types";
|
||||||
|
import { Placement } from "@popperjs/core";
|
||||||
// constants
|
// constants
|
||||||
import { PRIORITIES } from "constants/project";
|
import { PRIORITIES } from "constants/project";
|
||||||
|
|
||||||
@ -21,6 +22,7 @@ type Props = {
|
|||||||
className?: string;
|
className?: string;
|
||||||
buttonClassName?: string;
|
buttonClassName?: string;
|
||||||
optionsClassName?: string;
|
optionsClassName?: string;
|
||||||
|
placement?: Placement;
|
||||||
hideDropdownArrow?: boolean;
|
hideDropdownArrow?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
@ -31,14 +33,18 @@ export const PrioritySelect: React.FC<Props> = ({
|
|||||||
className = "",
|
className = "",
|
||||||
buttonClassName = "",
|
buttonClassName = "",
|
||||||
optionsClassName = "",
|
optionsClassName = "",
|
||||||
|
placement,
|
||||||
hideDropdownArrow = false,
|
hideDropdownArrow = false,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
}) => {
|
}) => {
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
const dropdownBtn = useRef<any>(null);
|
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
||||||
const dropdownOptions = useRef<any>(null);
|
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
|
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||||
|
placement: placement ?? "bottom-start",
|
||||||
|
});
|
||||||
|
|
||||||
const options = PRIORITIES?.map((priority) => ({
|
const options = PRIORITIES?.map((priority) => ({
|
||||||
value: priority,
|
value: priority,
|
||||||
@ -87,8 +93,6 @@ export const PrioritySelect: React.FC<Props> = ({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
|
||||||
useDynamicDropdownPosition(isOpen, () => setIsOpen(false), dropdownBtn, dropdownOptions);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Combobox
|
<Combobox
|
||||||
as="div"
|
as="div"
|
||||||
@ -97,77 +101,71 @@ export const PrioritySelect: React.FC<Props> = ({
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
{({ open }: { open: boolean }) => {
|
<Combobox.Button as={React.Fragment}>
|
||||||
if (open) {
|
<button
|
||||||
if (!isOpen) setIsOpen(true);
|
ref={setReferenceElement}
|
||||||
} else if (isOpen) setIsOpen(false);
|
type="button"
|
||||||
|
className={`flex items-center justify-between gap-1 w-full text-xs ${
|
||||||
return (
|
disabled
|
||||||
<>
|
? "cursor-not-allowed text-custom-text-200"
|
||||||
<Combobox.Button
|
: "cursor-pointer hover:bg-custom-background-80"
|
||||||
ref={dropdownBtn}
|
} ${buttonClassName}`}
|
||||||
type="button"
|
>
|
||||||
className={`flex items-center justify-between gap-1 w-full text-xs ${
|
{label}
|
||||||
disabled
|
{!hideDropdownArrow && !disabled && (
|
||||||
? "cursor-not-allowed text-custom-text-200"
|
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
||||||
: "cursor-pointer hover:bg-custom-background-80"
|
)}
|
||||||
} ${buttonClassName}`}
|
</button>
|
||||||
>
|
</Combobox.Button>
|
||||||
{label}
|
<Combobox.Options>
|
||||||
{!hideDropdownArrow && !disabled && (
|
<div
|
||||||
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
className={`z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-custom-shadow-rg focus:outline-none w-48 whitespace-nowrap my-1 ${optionsClassName}`}
|
||||||
)}
|
ref={setPopperElement}
|
||||||
</Combobox.Button>
|
style={styles.popper}
|
||||||
<div className={`${open ? "fixed z-20 top-0 left-0 h-full w-full cursor-auto" : ""}`}>
|
{...attributes.popper}
|
||||||
<Combobox.Options
|
>
|
||||||
ref={dropdownOptions}
|
<div className="flex w-full items-center justify-start rounded border border-custom-border-200 bg-custom-background-90 px-2">
|
||||||
className={`absolute z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-lg focus:outline-none w-48 whitespace-nowrap mt-1 ${optionsClassName}`}
|
<MagnifyingGlassIcon className="h-3.5 w-3.5 text-custom-text-300" />
|
||||||
>
|
<Combobox.Input
|
||||||
<div className="flex w-full items-center justify-start rounded border border-custom-border-200 bg-custom-background-90 px-2">
|
className="w-full bg-transparent py-1 px-2 text-xs text-custom-text-200 placeholder:text-custom-text-400 focus:outline-none"
|
||||||
<MagnifyingGlassIcon className="h-3.5 w-3.5 text-custom-text-300" />
|
value={query}
|
||||||
<Combobox.Input
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
className="w-full bg-transparent py-1 px-2 text-xs text-custom-text-200 placeholder:text-custom-text-400 focus:outline-none"
|
placeholder="Search"
|
||||||
value={query}
|
displayValue={(assigned: any) => assigned?.name}
|
||||||
onChange={(e) => setQuery(e.target.value)}
|
/>
|
||||||
placeholder="Search"
|
</div>
|
||||||
displayValue={(assigned: any) => assigned?.name}
|
<div className={`mt-2 space-y-1 max-h-48 overflow-y-scroll`}>
|
||||||
/>
|
{filteredOptions ? (
|
||||||
</div>
|
filteredOptions.length > 0 ? (
|
||||||
<div className={`mt-2 space-y-1 max-h-48 overflow-y-scroll`}>
|
filteredOptions.map((option) => (
|
||||||
{filteredOptions ? (
|
<Combobox.Option
|
||||||
filteredOptions.length > 0 ? (
|
key={option.value}
|
||||||
filteredOptions.map((option) => (
|
value={option.value}
|
||||||
<Combobox.Option
|
className={({ active, selected }) =>
|
||||||
key={option.value}
|
`flex items-center justify-between gap-2 cursor-pointer select-none truncate rounded px-1 py-1.5 ${
|
||||||
value={option.value}
|
active && !selected ? "bg-custom-background-80" : ""
|
||||||
className={({ active, selected }) =>
|
} ${selected ? "text-custom-text-100" : "text-custom-text-200"}`
|
||||||
`flex items-center justify-between gap-2 cursor-pointer select-none truncate rounded px-1 py-1.5 ${
|
}
|
||||||
active && !selected ? "bg-custom-background-80" : ""
|
>
|
||||||
} ${selected ? "text-custom-text-100" : "text-custom-text-200"}`
|
{({ selected }) => (
|
||||||
}
|
<>
|
||||||
>
|
{option.content}
|
||||||
{({ selected }) => (
|
{selected && <CheckIcon className={`h-3.5 w-3.5`} />}
|
||||||
<>
|
</>
|
||||||
{option.content}
|
)}
|
||||||
{selected && <CheckIcon className={`h-3.5 w-3.5`} />}
|
</Combobox.Option>
|
||||||
</>
|
))
|
||||||
)}
|
) : (
|
||||||
</Combobox.Option>
|
<span className="flex items-center gap-2 p-1">
|
||||||
))
|
<p className="text-left text-custom-text-200 ">No matching results</p>
|
||||||
) : (
|
</span>
|
||||||
<span className="flex items-center gap-2 p-1">
|
)
|
||||||
<p className="text-left text-custom-text-200 ">No matching results</p>
|
) : (
|
||||||
</span>
|
<p className="text-center text-custom-text-200">Loading...</p>
|
||||||
)
|
)}
|
||||||
) : (
|
</div>
|
||||||
<p className="text-center text-custom-text-200">Loading...</p>
|
</div>
|
||||||
)}
|
</Combobox.Options>
|
||||||
</div>
|
|
||||||
</Combobox.Options>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Combobox>
|
</Combobox>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -230,7 +230,6 @@ const SendProjectInvitationModal: React.FC<Props> = (props) => {
|
|||||||
onChange(val);
|
onChange(val);
|
||||||
}}
|
}}
|
||||||
options={options}
|
options={options}
|
||||||
position="left"
|
|
||||||
width="w-full min-w-[12rem]"
|
width="w-full min-w-[12rem]"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -252,12 +251,12 @@ const SendProjectInvitationModal: React.FC<Props> = (props) => {
|
|||||||
<CustomSelect
|
<CustomSelect
|
||||||
{...field}
|
{...field}
|
||||||
customButton={
|
customButton={
|
||||||
<button className="flex w-full items-center justify-between gap-1 rounded-md border border-custom-border-200 shadow-sm duration-300 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-80 focus:outline-none px-3 py-2.5 text-sm text-left">
|
<div className="flex w-full items-center justify-between gap-1 rounded-md border border-custom-border-200 shadow-sm duration-300 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-80 focus:outline-none px-3 py-2.5 text-sm text-left">
|
||||||
<span className="capitalize">
|
<span className="capitalize">
|
||||||
{field.value ? ROLE[field.value] : "Select role"}
|
{field.value ? ROLE[field.value] : "Select role"}
|
||||||
</span>
|
</span>
|
||||||
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
||||||
</button>
|
</div>
|
||||||
}
|
}
|
||||||
input
|
input
|
||||||
width="w-full"
|
width="w-full"
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
|
||||||
// hooks
|
// react-popper
|
||||||
import useDynamicDropdownPosition from "hooks/use-dynamic-dropdown";
|
import { usePopper } from "react-popper";
|
||||||
// services
|
// services
|
||||||
import stateService from "services/state.service";
|
import stateService from "services/state.service";
|
||||||
// headless ui
|
// headless ui
|
||||||
@ -16,6 +16,7 @@ import { CheckIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
|||||||
import { StateGroupIcon } from "components/icons";
|
import { StateGroupIcon } from "components/icons";
|
||||||
// types
|
// types
|
||||||
import { Tooltip } from "components/ui";
|
import { Tooltip } from "components/ui";
|
||||||
|
import { Placement } from "@popperjs/core";
|
||||||
// constants
|
// constants
|
||||||
import { IState } from "types";
|
import { IState } from "types";
|
||||||
import { STATES_LIST } from "constants/fetch-keys";
|
import { STATES_LIST } from "constants/fetch-keys";
|
||||||
@ -29,6 +30,7 @@ type Props = {
|
|||||||
className?: string;
|
className?: string;
|
||||||
buttonClassName?: string;
|
buttonClassName?: string;
|
||||||
optionsClassName?: string;
|
optionsClassName?: string;
|
||||||
|
placement?: Placement;
|
||||||
hideDropdownArrow?: boolean;
|
hideDropdownArrow?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
@ -40,17 +42,21 @@ export const StateSelect: React.FC<Props> = ({
|
|||||||
className = "",
|
className = "",
|
||||||
buttonClassName = "",
|
buttonClassName = "",
|
||||||
optionsClassName = "",
|
optionsClassName = "",
|
||||||
|
placement,
|
||||||
hideDropdownArrow = false,
|
hideDropdownArrow = false,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
}) => {
|
}) => {
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
const dropdownBtn = useRef<any>(null);
|
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
||||||
const dropdownOptions = useRef<any>(null);
|
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
const [fetchStates, setFetchStates] = useState<boolean>(false);
|
const [fetchStates, setFetchStates] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||||
|
placement: placement ?? "bottom-start",
|
||||||
|
});
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
@ -90,8 +96,6 @@ export const StateSelect: React.FC<Props> = ({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
|
||||||
useDynamicDropdownPosition(isOpen, () => setIsOpen(false), dropdownBtn, dropdownOptions);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Combobox
|
<Combobox
|
||||||
as="div"
|
as="div"
|
||||||
@ -103,31 +107,32 @@ export const StateSelect: React.FC<Props> = ({
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
{({ open }: { open: boolean }) => {
|
{({ open }: { open: boolean }) => {
|
||||||
if (open) {
|
if (open) setFetchStates(true);
|
||||||
if (!isOpen) setIsOpen(true);
|
|
||||||
setFetchStates(true);
|
|
||||||
} else if (isOpen) setIsOpen(false);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Combobox.Button
|
<Combobox.Button as={React.Fragment}>
|
||||||
ref={dropdownBtn}
|
<button
|
||||||
type="button"
|
ref={setReferenceElement}
|
||||||
className={`flex items-center justify-between gap-1 w-full text-xs px-2.5 py-1 rounded-md shadow-sm border border-custom-border-300 duration-300 focus:outline-none ${
|
type="button"
|
||||||
disabled
|
className={`flex items-center justify-between gap-1 w-full text-xs px-2.5 py-1 rounded-md shadow-sm border border-custom-border-300 duration-300 focus:outline-none ${
|
||||||
? "cursor-not-allowed text-custom-text-200"
|
disabled
|
||||||
: "cursor-pointer hover:bg-custom-background-80"
|
? "cursor-not-allowed text-custom-text-200"
|
||||||
} ${buttonClassName}`}
|
: "cursor-pointer hover:bg-custom-background-80"
|
||||||
>
|
} ${buttonClassName}`}
|
||||||
{label}
|
>
|
||||||
{!hideDropdownArrow && !disabled && (
|
{label}
|
||||||
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
{!hideDropdownArrow && !disabled && (
|
||||||
)}
|
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
</Combobox.Button>
|
</Combobox.Button>
|
||||||
<div className={`${open ? "fixed z-20 top-0 left-0 h-full w-full cursor-auto" : ""}`}>
|
<Combobox.Options>
|
||||||
<Combobox.Options
|
<div
|
||||||
ref={dropdownOptions}
|
className={`z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-custom-shadow-rg focus:outline-none w-48 whitespace-nowrap my-1 ${optionsClassName}`}
|
||||||
className={`absolute z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-lg focus:outline-none w-48 whitespace-nowrap mt-1 ${optionsClassName}`}
|
ref={setPopperElement}
|
||||||
|
style={styles.popper}
|
||||||
|
{...attributes.popper}
|
||||||
>
|
>
|
||||||
<div className="flex w-full items-center justify-start rounded border border-custom-border-200 bg-custom-background-90 px-2">
|
<div className="flex w-full items-center justify-start rounded border border-custom-border-200 bg-custom-background-90 px-2">
|
||||||
<MagnifyingGlassIcon className="h-3.5 w-3.5 text-custom-text-300" />
|
<MagnifyingGlassIcon className="h-3.5 w-3.5 text-custom-text-300" />
|
||||||
@ -169,8 +174,8 @@ export const StateSelect: React.FC<Props> = ({
|
|||||||
<p className="text-center text-custom-text-200">Loading...</p>
|
<p className="text-center text-custom-text-200">Loading...</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Combobox.Options>
|
</div>
|
||||||
</div>
|
</Combobox.Options>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
|
// react-poppper
|
||||||
|
import { usePopper } from "react-popper";
|
||||||
// headless ui
|
// headless ui
|
||||||
import { Menu, Transition } from "@headlessui/react";
|
import { Menu } from "@headlessui/react";
|
||||||
// ui
|
// ui
|
||||||
import { DropdownProps } from "components/ui";
|
import { DropdownProps } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
@ -20,6 +22,7 @@ export type CustomMenuProps = DropdownProps & {
|
|||||||
const CustomMenu = ({
|
const CustomMenu = ({
|
||||||
buttonClassName = "",
|
buttonClassName = "",
|
||||||
customButtonClassName = "",
|
customButtonClassName = "",
|
||||||
|
placement,
|
||||||
children,
|
children,
|
||||||
className = "",
|
className = "",
|
||||||
customButton,
|
customButton,
|
||||||
@ -30,100 +33,106 @@ const CustomMenu = ({
|
|||||||
noBorder = false,
|
noBorder = false,
|
||||||
noChevron = false,
|
noChevron = false,
|
||||||
optionsClassName = "",
|
optionsClassName = "",
|
||||||
position = "right",
|
|
||||||
selfPositioned = false,
|
|
||||||
verticalEllipsis = false,
|
verticalEllipsis = false,
|
||||||
verticalPosition = "bottom",
|
|
||||||
width = "auto",
|
width = "auto",
|
||||||
menuButtonOnClick,
|
menuButtonOnClick,
|
||||||
}: CustomMenuProps) => (
|
}: CustomMenuProps) => {
|
||||||
<Menu as="div" className={`${selfPositioned ? "" : "relative"} w-min text-left ${className}`}>
|
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
||||||
{({ open }) => (
|
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||||
<>
|
|
||||||
{customButton ? (
|
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||||
<Menu.Button
|
placement: placement ?? "bottom-start",
|
||||||
as="button"
|
});
|
||||||
type="button"
|
return (
|
||||||
onClick={menuButtonOnClick}
|
<Menu as="div" className={`relative w-min text-left ${className}`}>
|
||||||
className={customButtonClassName}
|
{({ open }) => (
|
||||||
disabled={disabled}
|
<>
|
||||||
>
|
{customButton ? (
|
||||||
{customButton}
|
<Menu.Button as={React.Fragment}>
|
||||||
</Menu.Button>
|
<button
|
||||||
) : (
|
ref={setReferenceElement}
|
||||||
<>
|
|
||||||
{ellipsis || verticalEllipsis ? (
|
|
||||||
<Menu.Button
|
|
||||||
type="button"
|
type="button"
|
||||||
onClick={menuButtonOnClick}
|
onClick={menuButtonOnClick}
|
||||||
disabled={disabled}
|
className={customButtonClassName}
|
||||||
className={`relative grid place-items-center rounded p-1 text-custom-text-200 hover:text-custom-text-100 outline-none ${
|
|
||||||
disabled ? "cursor-not-allowed" : "cursor-pointer hover:bg-custom-background-80"
|
|
||||||
} ${buttonClassName}`}
|
|
||||||
>
|
>
|
||||||
<MoreHorizOutlined
|
{customButton}
|
||||||
fontSize="small"
|
</button>
|
||||||
className={verticalEllipsis ? "rotate-90" : ""}
|
</Menu.Button>
|
||||||
/>
|
) : (
|
||||||
</Menu.Button>
|
<>
|
||||||
) : (
|
{ellipsis || verticalEllipsis ? (
|
||||||
<Menu.Button
|
<Menu.Button as={React.Fragment}>
|
||||||
type="button"
|
<button
|
||||||
className={`flex items-center justify-between gap-1 rounded-md px-2.5 py-1 text-xs whitespace-nowrap duration-300 ${
|
ref={setReferenceElement}
|
||||||
open ? "bg-custom-background-90 text-custom-text-100" : "text-custom-text-200"
|
type="button"
|
||||||
} ${
|
onClick={menuButtonOnClick}
|
||||||
noBorder ? "" : "border border-custom-border-300 shadow-sm focus:outline-none"
|
disabled={disabled}
|
||||||
} ${
|
className={`relative grid place-items-center rounded p-1 text-custom-text-200 hover:text-custom-text-100 outline-none ${
|
||||||
disabled
|
disabled
|
||||||
? "cursor-not-allowed text-custom-text-200"
|
? "cursor-not-allowed"
|
||||||
: "cursor-pointer hover:bg-custom-background-80"
|
: "cursor-pointer hover:bg-custom-background-80"
|
||||||
} ${buttonClassName}`}
|
} ${buttonClassName}`}
|
||||||
>
|
>
|
||||||
{label}
|
<MoreHorizOutlined
|
||||||
{!noChevron && (
|
fontSize="small"
|
||||||
<ExpandMoreOutlined
|
className={verticalEllipsis ? "rotate-90" : ""}
|
||||||
sx={{
|
/>
|
||||||
fontSize: 14,
|
</button>
|
||||||
}}
|
</Menu.Button>
|
||||||
aria-hidden="true"
|
) : (
|
||||||
/>
|
<Menu.Button as={React.Fragment}>
|
||||||
)}
|
<button
|
||||||
</Menu.Button>
|
ref={setReferenceElement}
|
||||||
)}
|
type="button"
|
||||||
</>
|
className={`flex items-center justify-between gap-1 rounded-md px-2.5 py-1 text-xs whitespace-nowrap duration-300 ${
|
||||||
)}
|
open ? "bg-custom-background-90 text-custom-text-100" : "text-custom-text-200"
|
||||||
|
} ${
|
||||||
<Transition
|
noBorder ? "" : "border border-custom-border-300 shadow-sm focus:outline-none"
|
||||||
as={React.Fragment}
|
} ${
|
||||||
enter="transition ease-out duration-100"
|
disabled
|
||||||
enterFrom="transform opacity-0 scale-95"
|
? "cursor-not-allowed text-custom-text-200"
|
||||||
enterTo="transform opacity-100 scale-100"
|
: "cursor-pointer hover:bg-custom-background-80"
|
||||||
leave="transition ease-in duration-75"
|
} ${buttonClassName}`}
|
||||||
leaveFrom="transform opacity-100 scale-100"
|
>
|
||||||
leaveTo="transform opacity-0 scale-95"
|
{label}
|
||||||
>
|
{!noChevron && (
|
||||||
<Menu.Items
|
<ExpandMoreOutlined
|
||||||
className={`absolute z-10 overflow-y-scroll whitespace-nowrap rounded-md border border-custom-border-300 p-1 text-xs shadow-lg focus:outline-none bg-custom-background-90 ${
|
sx={{
|
||||||
position === "left" ? "left-0 origin-top-left" : "right-0 origin-top-right"
|
fontSize: 14,
|
||||||
} ${verticalPosition === "top" ? "bottom-full mb-1" : "mt-1"} ${
|
}}
|
||||||
maxHeight === "lg"
|
aria-hidden="true"
|
||||||
? "max-h-60"
|
/>
|
||||||
: maxHeight === "md"
|
)}
|
||||||
? "max-h-48"
|
</button>
|
||||||
: maxHeight === "rg"
|
</Menu.Button>
|
||||||
? "max-h-36"
|
)}
|
||||||
: maxHeight === "sm"
|
</>
|
||||||
? "max-h-28"
|
)}
|
||||||
: ""
|
<Menu.Items>
|
||||||
} ${width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width} ${optionsClassName}`}
|
<div
|
||||||
>
|
className={`z-10 overflow-y-scroll whitespace-nowrap rounded-md border border-custom-border-300 p-1 text-xs shadow-custom-shadow-rg focus:outline-none bg-custom-background-90 my-1 ${
|
||||||
<div className="py-1">{children}</div>
|
maxHeight === "lg"
|
||||||
|
? "max-h-60"
|
||||||
|
: maxHeight === "md"
|
||||||
|
? "max-h-48"
|
||||||
|
: maxHeight === "rg"
|
||||||
|
? "max-h-36"
|
||||||
|
: maxHeight === "sm"
|
||||||
|
? "max-h-28"
|
||||||
|
: ""
|
||||||
|
} ${width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width} ${optionsClassName}`}
|
||||||
|
ref={setPopperElement}
|
||||||
|
style={styles.popper}
|
||||||
|
{...attributes.popper}
|
||||||
|
>
|
||||||
|
<div className="py-1">{children}</div>
|
||||||
|
</div>
|
||||||
</Menu.Items>
|
</Menu.Items>
|
||||||
</Transition>
|
</>
|
||||||
</>
|
)}
|
||||||
)}
|
</Menu>
|
||||||
</Menu>
|
);
|
||||||
);
|
};
|
||||||
|
|
||||||
type MenuItemProps = {
|
type MenuItemProps = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
// react-poppper
|
||||||
|
import { usePopper } from "react-popper";
|
||||||
// headless ui
|
// headless ui
|
||||||
import { Combobox, Transition } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
// icons
|
// icons
|
||||||
import { ChevronDownIcon } from "@heroicons/react/20/solid";
|
import { ChevronDownIcon } from "@heroicons/react/20/solid";
|
||||||
import { CheckIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
import { CheckIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
||||||
@ -27,9 +29,11 @@ export type CustomSearchSelectProps = DropdownProps & {
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const CustomSearchSelect = ({
|
export const CustomSearchSelect = ({
|
||||||
|
customButtonClassName = "",
|
||||||
buttonClassName = "",
|
buttonClassName = "",
|
||||||
className = "",
|
className = "",
|
||||||
customButton,
|
customButton,
|
||||||
|
placement,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
footerOption,
|
footerOption,
|
||||||
input = false,
|
input = false,
|
||||||
@ -41,14 +45,18 @@ export const CustomSearchSelect = ({
|
|||||||
options,
|
options,
|
||||||
onOpen,
|
onOpen,
|
||||||
optionsClassName = "",
|
optionsClassName = "",
|
||||||
position = "left",
|
|
||||||
selfPositioned = false,
|
|
||||||
value,
|
value,
|
||||||
verticalPosition = "bottom",
|
|
||||||
width = "auto",
|
width = "auto",
|
||||||
}: CustomSearchSelectProps) => {
|
}: CustomSearchSelectProps) => {
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
|
|
||||||
|
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
||||||
|
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
|
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||||
|
placement: placement ?? "bottom-start",
|
||||||
|
});
|
||||||
|
|
||||||
const filteredOptions =
|
const filteredOptions =
|
||||||
query === ""
|
query === ""
|
||||||
? options
|
? options
|
||||||
@ -63,51 +71,54 @@ export const CustomSearchSelect = ({
|
|||||||
if (multiple) props.multiple = true;
|
if (multiple) props.multiple = true;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Combobox
|
<Combobox as="div" className={`relative flex-shrink-0 text-left ${className}`} {...props}>
|
||||||
as="div"
|
|
||||||
className={`${selfPositioned ? "" : "relative"} flex-shrink-0 text-left ${className}`}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{({ open }: { open: boolean }) => {
|
{({ open }: { open: boolean }) => {
|
||||||
if (open && onOpen) onOpen();
|
if (open && onOpen) onOpen();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{customButton ? (
|
{customButton ? (
|
||||||
<Combobox.Button as="div">{customButton}</Combobox.Button>
|
<Combobox.Button as={React.Fragment}>
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={`flex items-center justify-between gap-1 w-full text-xs ${
|
||||||
|
disabled
|
||||||
|
? "cursor-not-allowed text-custom-text-200"
|
||||||
|
: "cursor-pointer hover:bg-custom-background-80"
|
||||||
|
} ${customButtonClassName}`}
|
||||||
|
>
|
||||||
|
{customButton}
|
||||||
|
</button>
|
||||||
|
</Combobox.Button>
|
||||||
) : (
|
) : (
|
||||||
<Combobox.Button
|
<Combobox.Button as={React.Fragment}>
|
||||||
type="button"
|
<button
|
||||||
className={`flex items-center justify-between gap-1 w-full rounded-md shadow-sm border border-custom-border-300 duration-300 focus:outline-none ${
|
ref={setReferenceElement}
|
||||||
input ? "px-3 py-2 text-sm" : "px-2.5 py-1 text-xs"
|
type="button"
|
||||||
} ${
|
className={`flex items-center justify-between gap-1 w-full rounded-md border border-custom-border-300 shadow-sm duration-300 focus:outline-none ${
|
||||||
disabled
|
input ? "px-3 py-2 text-sm" : "px-2.5 py-1 text-xs"
|
||||||
? "cursor-not-allowed text-custom-text-200"
|
} ${
|
||||||
: "cursor-pointer hover:bg-custom-background-80"
|
disabled
|
||||||
} ${buttonClassName}`}
|
? "cursor-not-allowed text-custom-text-200"
|
||||||
>
|
: "cursor-pointer hover:bg-custom-background-80"
|
||||||
{label}
|
} ${buttonClassName}`}
|
||||||
{!noChevron && !disabled && (
|
>
|
||||||
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
{label}
|
||||||
)}
|
{!noChevron && !disabled && (
|
||||||
|
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
</Combobox.Button>
|
</Combobox.Button>
|
||||||
)}
|
)}
|
||||||
<Transition
|
<Combobox.Options as={React.Fragment}>
|
||||||
show={open}
|
<div
|
||||||
as={React.Fragment}
|
className={`z-10 min-w-[10rem] border border-custom-border-300 p-2 rounded-md bg-custom-background-90 text-xs shadow-custom-shadow-rg focus:outline-none my-1 ${
|
||||||
enter="transition ease-out duration-200"
|
|
||||||
enterFrom="opacity-0 translate-y-1"
|
|
||||||
enterTo="opacity-100 translate-y-0"
|
|
||||||
leave="transition ease-in duration-150"
|
|
||||||
leaveFrom="opacity-100 translate-y-0"
|
|
||||||
leaveTo="opacity-0 translate-y-1"
|
|
||||||
>
|
|
||||||
<Combobox.Options
|
|
||||||
className={`absolute z-10 min-w-[10rem] border border-custom-border-300 p-2 rounded-md bg-custom-background-90 text-xs shadow-lg focus:outline-none ${
|
|
||||||
position === "left" ? "left-0 origin-top-left" : "right-0 origin-top-right"
|
|
||||||
} ${verticalPosition === "top" ? "bottom-full mb-1" : "mt-1"} ${
|
|
||||||
width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width
|
width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width
|
||||||
} ${optionsClassName}`}
|
} ${optionsClassName}`}
|
||||||
|
ref={setPopperElement}
|
||||||
|
style={styles.popper}
|
||||||
|
{...attributes.popper}
|
||||||
>
|
>
|
||||||
<div className="flex w-full items-center justify-start rounded-sm border-[0.6px] border-custom-border-200 bg-custom-background-90 px-2">
|
<div className="flex w-full items-center justify-start rounded-sm border-[0.6px] border-custom-border-200 bg-custom-background-90 px-2">
|
||||||
<MagnifyingGlassIcon className="h-3 w-3 text-custom-text-200" />
|
<MagnifyingGlassIcon className="h-3 w-3 text-custom-text-200" />
|
||||||
@ -176,8 +187,8 @@ export const CustomSearchSelect = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{footerOption}
|
{footerOption}
|
||||||
</Combobox.Options>
|
</div>
|
||||||
</Transition>
|
</Combobox.Options>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
// react-popper
|
||||||
|
import { usePopper } from "react-popper";
|
||||||
// headless ui
|
// headless ui
|
||||||
import { Listbox, Transition } from "@headlessui/react";
|
import { Listbox } from "@headlessui/react";
|
||||||
// icons
|
// icons
|
||||||
import { ChevronDownIcon } from "@heroicons/react/20/solid";
|
import { ChevronDownIcon } from "@heroicons/react/20/solid";
|
||||||
import { CheckIcon } from "@heroicons/react/24/outline";
|
import { CheckIcon } from "@heroicons/react/24/outline";
|
||||||
@ -15,7 +17,9 @@ export type CustomSelectProps = DropdownProps & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const CustomSelect = ({
|
const CustomSelect = ({
|
||||||
|
customButtonClassName = "",
|
||||||
buttonClassName = "",
|
buttonClassName = "",
|
||||||
|
placement,
|
||||||
children,
|
children,
|
||||||
className = "",
|
className = "",
|
||||||
customButton,
|
customButton,
|
||||||
@ -26,68 +30,83 @@ const CustomSelect = ({
|
|||||||
noChevron = false,
|
noChevron = false,
|
||||||
onChange,
|
onChange,
|
||||||
optionsClassName = "",
|
optionsClassName = "",
|
||||||
position = "left",
|
|
||||||
selfPositioned = false,
|
|
||||||
value,
|
value,
|
||||||
verticalPosition = "bottom",
|
|
||||||
width = "auto",
|
width = "auto",
|
||||||
}: CustomSelectProps) => (
|
}: CustomSelectProps) => {
|
||||||
<Listbox
|
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
||||||
as="div"
|
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
className={`${selfPositioned ? "" : "relative"} flex-shrink-0 text-left ${className}`}
|
|
||||||
disabled={disabled}
|
|
||||||
>
|
|
||||||
<>
|
|
||||||
{customButton ? (
|
|
||||||
<Listbox.Button as={React.Fragment}>{customButton}</Listbox.Button>
|
|
||||||
) : (
|
|
||||||
<Listbox.Button
|
|
||||||
type="button"
|
|
||||||
className={`flex items-center justify-between gap-1 w-full rounded-md border border-custom-border-300 shadow-sm duration-300 focus:outline-none ${
|
|
||||||
input ? "px-3 py-2 text-sm" : "px-2.5 py-1 text-xs"
|
|
||||||
} ${
|
|
||||||
disabled
|
|
||||||
? "cursor-not-allowed text-custom-text-200"
|
|
||||||
: "cursor-pointer hover:bg-custom-background-80"
|
|
||||||
} ${buttonClassName}`}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
{!noChevron && !disabled && <ChevronDownIcon className="h-3 w-3" aria-hidden="true" />}
|
|
||||||
</Listbox.Button>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
|
|
||||||
<Transition
|
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||||
as={React.Fragment}
|
placement: placement ?? "bottom-start",
|
||||||
enter="transition ease-out duration-100"
|
});
|
||||||
enterFrom="transform opacity-0 scale-95"
|
|
||||||
enterTo="transform opacity-100 scale-100"
|
return (
|
||||||
leave="transition ease-in duration-75"
|
<Listbox
|
||||||
leaveFrom="transform opacity-100 scale-100"
|
as="div"
|
||||||
leaveTo="transform opacity-0 scale-95"
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
className={`relative flex-shrink-0 text-left ${className}`}
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<Listbox.Options
|
<>
|
||||||
className={`absolute z-10 border border-custom-border-300 mt-1 origin-top-right overflow-y-auto rounded-md bg-custom-background-90 text-xs shadow-lg focus:outline-none ${
|
{customButton ? (
|
||||||
position === "left" ? "left-0 origin-top-left" : "right-0 origin-top-right"
|
<Listbox.Button as={React.Fragment}>
|
||||||
} ${verticalPosition === "top" ? "bottom-full mb-1" : "mt-1"} ${
|
<button
|
||||||
maxHeight === "lg"
|
ref={setReferenceElement}
|
||||||
? "max-h-60"
|
type="button"
|
||||||
: maxHeight === "md"
|
className={`flex items-center justify-between gap-1 w-full text-xs ${
|
||||||
? "max-h-48"
|
disabled
|
||||||
: maxHeight === "rg"
|
? "cursor-not-allowed text-custom-text-200"
|
||||||
? "max-h-36"
|
: "cursor-pointer hover:bg-custom-background-80"
|
||||||
: maxHeight === "sm"
|
} ${customButtonClassName}`}
|
||||||
? "max-h-28"
|
>
|
||||||
: ""
|
{customButton}
|
||||||
} ${width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width} ${optionsClassName}`}
|
</button>
|
||||||
>
|
</Listbox.Button>
|
||||||
<div className="space-y-1 p-2">{children}</div>
|
) : (
|
||||||
|
<Listbox.Button as={React.Fragment}>
|
||||||
|
<button
|
||||||
|
ref={setReferenceElement}
|
||||||
|
type="button"
|
||||||
|
className={`flex items-center justify-between gap-1 w-full rounded-md border border-custom-border-300 shadow-sm duration-300 focus:outline-none ${
|
||||||
|
input ? "px-3 py-2 text-sm" : "px-2.5 py-1 text-xs"
|
||||||
|
} ${
|
||||||
|
disabled
|
||||||
|
? "cursor-not-allowed text-custom-text-200"
|
||||||
|
: "cursor-pointer hover:bg-custom-background-80"
|
||||||
|
} ${buttonClassName}`}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
{!noChevron && !disabled && (
|
||||||
|
<ChevronDownIcon className="h-3 w-3" aria-hidden="true" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</Listbox.Button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
<Listbox.Options>
|
||||||
|
<div
|
||||||
|
className={`z-10 border border-custom-border-300 overflow-y-auto rounded-md bg-custom-background-90 text-xs shadow-custom-shadow-rg focus:outline-none my-1 ${
|
||||||
|
maxHeight === "lg"
|
||||||
|
? "max-h-60"
|
||||||
|
: maxHeight === "md"
|
||||||
|
? "max-h-48"
|
||||||
|
: maxHeight === "rg"
|
||||||
|
? "max-h-36"
|
||||||
|
: maxHeight === "sm"
|
||||||
|
? "max-h-28"
|
||||||
|
: ""
|
||||||
|
} ${width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width} ${optionsClassName}`}
|
||||||
|
ref={setPopperElement}
|
||||||
|
style={styles.popper}
|
||||||
|
{...attributes.popper}
|
||||||
|
>
|
||||||
|
<div className="space-y-1 p-2">{children}</div>
|
||||||
|
</div>
|
||||||
</Listbox.Options>
|
</Listbox.Options>
|
||||||
</Transition>
|
</Listbox>
|
||||||
</Listbox>
|
);
|
||||||
);
|
};
|
||||||
|
|
||||||
type OptionProps = {
|
type OptionProps = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
7
web/components/ui/dropdowns/types.d.ts
vendored
7
web/components/ui/dropdowns/types.d.ts
vendored
@ -1,4 +1,7 @@
|
|||||||
|
import { Placement } from "@popperjs/core";
|
||||||
|
|
||||||
export type DropdownProps = {
|
export type DropdownProps = {
|
||||||
|
customButtonClassName?: string;
|
||||||
buttonClassName?: string;
|
buttonClassName?: string;
|
||||||
customButtonClassName?: string;
|
customButtonClassName?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -10,8 +13,6 @@ export type DropdownProps = {
|
|||||||
noChevron?: boolean;
|
noChevron?: boolean;
|
||||||
onOpen?: () => void;
|
onOpen?: () => void;
|
||||||
optionsClassName?: string;
|
optionsClassName?: string;
|
||||||
position?: "right" | "left";
|
|
||||||
selfPositioned?: boolean;
|
|
||||||
verticalPosition?: "top" | "bottom";
|
|
||||||
width?: "auto" | string;
|
width?: "auto" | string;
|
||||||
|
placement?: Placement;
|
||||||
};
|
};
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
"@nivo/line": "0.80.0",
|
"@nivo/line": "0.80.0",
|
||||||
"@nivo/pie": "0.80.0",
|
"@nivo/pie": "0.80.0",
|
||||||
"@nivo/scatterplot": "0.80.0",
|
"@nivo/scatterplot": "0.80.0",
|
||||||
|
"@popperjs/core": "^2.11.8",
|
||||||
"@sentry/nextjs": "^7.36.0",
|
"@sentry/nextjs": "^7.36.0",
|
||||||
"@tiptap/extension-code-block-lowlight": "^2.0.4",
|
"@tiptap/extension-code-block-lowlight": "^2.0.4",
|
||||||
"@tiptap/extension-color": "^2.0.4",
|
"@tiptap/extension-color": "^2.0.4",
|
||||||
@ -72,6 +73,7 @@
|
|||||||
"react-hook-form": "^7.38.0",
|
"react-hook-form": "^7.38.0",
|
||||||
"react-markdown": "^8.0.7",
|
"react-markdown": "^8.0.7",
|
||||||
"react-moveable": "^0.54.1",
|
"react-moveable": "^0.54.1",
|
||||||
|
"react-popper": "^2.3.0",
|
||||||
"sharp": "^0.32.1",
|
"sharp": "^0.32.1",
|
||||||
"sonner": "^0.6.2",
|
"sonner": "^0.6.2",
|
||||||
"swr": "^2.1.3",
|
"swr": "^2.1.3",
|
||||||
|
@ -298,8 +298,6 @@ const Profile: NextPage = () => {
|
|||||||
buttonClassName={errors.role ? "border-red-500 bg-red-500/10" : ""}
|
buttonClassName={errors.role ? "border-red-500 bg-red-500/10" : ""}
|
||||||
width="w-full"
|
width="w-full"
|
||||||
input
|
input
|
||||||
verticalPosition="top"
|
|
||||||
position="right"
|
|
||||||
>
|
>
|
||||||
{USER_ROLES.map((item) => (
|
{USER_ROLES.map((item) => (
|
||||||
<CustomSelect.Option key={item.value} value={item.value}>
|
<CustomSelect.Option key={item.value} value={item.value}>
|
||||||
@ -362,7 +360,6 @@ const Profile: NextPage = () => {
|
|||||||
}
|
}
|
||||||
options={timeZoneOptions}
|
options={timeZoneOptions}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
verticalPosition="top"
|
|
||||||
optionsClassName="w-full"
|
optionsClassName="w-full"
|
||||||
input
|
input
|
||||||
/>
|
/>
|
||||||
|
@ -427,13 +427,10 @@ const SinglePage: NextPage = () => {
|
|||||||
)}
|
)}
|
||||||
<CustomSearchSelect
|
<CustomSearchSelect
|
||||||
customButton={
|
customButton={
|
||||||
<button
|
<div className="flex items-center gap-1 rounded-sm bg-custom-background-80 p-1.5 text-xs">
|
||||||
type="button"
|
|
||||||
className="flex items-center gap-1 rounded-sm bg-custom-background-80 p-1.5 text-xs"
|
|
||||||
>
|
|
||||||
<PlusIcon className="h-3.5 w-3.5" />
|
<PlusIcon className="h-3.5 w-3.5" />
|
||||||
{pageDetails.labels.length <= 0 && <span>Add Label</span>}
|
{pageDetails.labels.length <= 0 && <span>Add Label</span>}
|
||||||
</button>
|
</div>
|
||||||
}
|
}
|
||||||
value={pageDetails.labels}
|
value={pageDetails.labels}
|
||||||
footerOption={
|
footerOption={
|
||||||
|
@ -412,7 +412,7 @@ const MembersSettings: NextPage = () => {
|
|||||||
)}
|
)}
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
customButton={
|
customButton={
|
||||||
<button className="flex item-center gap-1">
|
<div className="flex item-center gap-1">
|
||||||
<span
|
<span
|
||||||
className={`flex items-center text-sm font-medium ${
|
className={`flex items-center text-sm font-medium ${
|
||||||
member.memberId !== user?.id ? "" : "text-custom-sidebar-text-400"
|
member.memberId !== user?.id ? "" : "text-custom-sidebar-text-400"
|
||||||
@ -423,7 +423,7 @@ const MembersSettings: NextPage = () => {
|
|||||||
{member.memberId !== user?.id && (
|
{member.memberId !== user?.id && (
|
||||||
<Icon iconName="expand_more" className="text-lg font-medium" />
|
<Icon iconName="expand_more" className="text-lg font-medium" />
|
||||||
)}
|
)}
|
||||||
</button>
|
</div>
|
||||||
}
|
}
|
||||||
value={member.role}
|
value={member.role}
|
||||||
onChange={(value: 5 | 10 | 15 | 20 | undefined) => {
|
onChange={(value: 5 | 10 | 15 | 20 | undefined) => {
|
||||||
@ -455,7 +455,6 @@ const MembersSettings: NextPage = () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
position="right"
|
|
||||||
disabled={
|
disabled={
|
||||||
member.memberId === user?.id ||
|
member.memberId === user?.id ||
|
||||||
!member.member ||
|
!member.member ||
|
||||||
|
@ -269,7 +269,7 @@ const MembersSettings: NextPage = () => {
|
|||||||
)}
|
)}
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
customButton={
|
customButton={
|
||||||
<button className="flex item-center gap-1">
|
<div className="flex item-center gap-1">
|
||||||
<span
|
<span
|
||||||
className={`flex items-center text-sm font-medium ${
|
className={`flex items-center text-sm font-medium ${
|
||||||
member.memberId !== user?.id ? "" : "text-custom-sidebar-text-400"
|
member.memberId !== user?.id ? "" : "text-custom-sidebar-text-400"
|
||||||
@ -280,7 +280,7 @@ const MembersSettings: NextPage = () => {
|
|||||||
{member.memberId !== user?.id && (
|
{member.memberId !== user?.id && (
|
||||||
<Icon iconName="expand_more" className="text-lg font-medium" />
|
<Icon iconName="expand_more" className="text-lg font-medium" />
|
||||||
)}
|
)}
|
||||||
</button>
|
</div>
|
||||||
}
|
}
|
||||||
value={member.role}
|
value={member.role}
|
||||||
onChange={(value: 5 | 10 | 15 | 20 | undefined) => {
|
onChange={(value: 5 | 10 | 15 | 20 | undefined) => {
|
||||||
@ -307,7 +307,6 @@ const MembersSettings: NextPage = () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
position="right"
|
|
||||||
disabled={
|
disabled={
|
||||||
member.memberId === currentUser?.member.id ||
|
member.memberId === currentUser?.member.id ||
|
||||||
!member.status ||
|
!member.status ||
|
||||||
|
@ -6628,20 +6628,13 @@ prosemirror-menu@^1.2.1:
|
|||||||
prosemirror-history "^1.0.0"
|
prosemirror-history "^1.0.0"
|
||||||
prosemirror-state "^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"
|
version "1.18.1"
|
||||||
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.18.1.tgz#1d5d6b6de7b983ee67a479dc607165fdef3935bd"
|
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.18.1.tgz#1d5d6b6de7b983ee67a479dc607165fdef3935bd"
|
||||||
integrity sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw==
|
integrity sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw==
|
||||||
dependencies:
|
dependencies:
|
||||||
orderedmap "^2.0.0"
|
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:
|
prosemirror-schema-basic@^1.2.0:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.2.tgz#6695f5175e4628aab179bf62e5568628b9cfe6c7"
|
resolved "https://registry.yarnpkg.com/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.2.tgz#6695f5175e4628aab179bf62e5568628b9cfe6c7"
|
||||||
|
Loading…
Reference in New Issue
Block a user