style: issue modal select attributes

This commit is contained in:
Aaryan Khandelwal 2023-09-19 19:04:54 +05:30
parent 9ef30b4fbf
commit 5dc9e00c3d
12 changed files with 84 additions and 60 deletions

View File

@ -5,6 +5,7 @@ import { ICustomAttribute } from "types";
type Props = { type Props = {
attributeDetails: ICustomAttribute; attributeDetails: ICustomAttribute;
className?: string;
issueId: string; issueId: string;
projectId: string; projectId: string;
value: Date | undefined; value: Date | undefined;
@ -22,12 +23,15 @@ const TIME_FORMATS: { [key: string]: string } = {
"24": "HH:mm", "24": "HH:mm",
}; };
export const CustomDateTimeAttribute: React.FC<Props> = ({ attributeDetails, onChange, value }) => ( export const CustomDateTimeAttribute: React.FC<Props> = (props) => {
const { attributeDetails, className = "", onChange, value } = props;
return (
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<ReactDatePicker <ReactDatePicker
selected={value} selected={value}
onChange={onChange} onChange={onChange}
className="bg-custom-background-80 rounded text-xs px-2.5 py-0.5 outline-none truncate" className={`bg-custom-background-80 rounded text-xs px-2.5 py-0.5 outline-none truncate ${className}`}
calendarClassName="!bg-custom-background-80" calendarClassName="!bg-custom-background-80"
dateFormat={`${ dateFormat={`${
attributeDetails.extra_settings.hide_date attributeDetails.extra_settings.hide_date
@ -44,3 +48,4 @@ export const CustomDateTimeAttribute: React.FC<Props> = ({ attributeDetails, onC
/> />
</div> </div>
); );
};

View File

@ -13,12 +13,13 @@ import useWorkspaceDetails from "hooks/use-workspace-details";
import { getFileIcon } from "components/icons"; import { getFileIcon } from "components/icons";
import { X } from "lucide-react"; import { X } from "lucide-react";
// helpers // helpers
import { getFileExtension, getFileName } from "helpers/attachment.helper"; import { getFileExtension } from "helpers/attachment.helper";
// types // types
import { ICustomAttribute } from "types"; import { ICustomAttribute } from "types";
type Props = { type Props = {
attributeDetails: ICustomAttribute; attributeDetails: ICustomAttribute;
className?: string;
issueId: string; issueId: string;
projectId: string; projectId: string;
value: string | undefined; value: string | undefined;
@ -28,7 +29,7 @@ type Props = {
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5 MB const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5 MB
export const CustomFileAttribute: React.FC<Props> = (props) => { export const CustomFileAttribute: React.FC<Props> = (props) => {
const { attributeDetails, onChange, value } = props; const { attributeDetails, className = "", onChange, value } = props;
const [isUploading, setIsUploading] = useState(false); const [isUploading, setIsUploading] = useState(false);
@ -136,7 +137,7 @@ export const CustomFileAttribute: React.FC<Props> = (props) => {
{...getRootProps()} {...getRootProps()}
className={`flex items-center bg-custom-background-80 text-xs rounded px-2.5 py-0.5 cursor-pointer truncate w-min max-w-full whitespace-nowrap ${ className={`flex items-center bg-custom-background-80 text-xs rounded px-2.5 py-0.5 cursor-pointer truncate w-min max-w-full whitespace-nowrap ${
isDragActive ? "bg-custom-primary-100/10" : "" isDragActive ? "bg-custom-primary-100/10" : ""
} ${isDragReject ? "bg-red-500/10" : ""}`} } ${isDragReject ? "bg-red-500/10" : ""} ${className}`}
> >
<input className="flex-shrink-0" {...getInputProps()} /> <input className="flex-shrink-0" {...getInputProps()} />
<span className={`flex-grow truncate text-left ${fileError ? "text-red-500" : ""}`}> <span className={`flex-grow truncate text-left ${fileError ? "text-red-500" : ""}`}>

View File

@ -15,9 +15,12 @@ import { Search } from "lucide-react";
import { ICustomAttribute } from "types"; import { ICustomAttribute } from "types";
// fetch-keys // fetch-keys
import { CYCLES_LIST, MODULE_LIST } from "constants/fetch-keys"; import { CYCLES_LIST, MODULE_LIST } from "constants/fetch-keys";
import useProjectMembers from "hooks/use-project-members";
import { Avatar } from "components/ui";
type Props = { type Props = {
attributeDetails: ICustomAttribute; attributeDetails: ICustomAttribute;
className?: string;
issueId: string; issueId: string;
projectId: string; projectId: string;
value: string | undefined; value: string | undefined;
@ -26,6 +29,7 @@ type Props = {
export const CustomRelationAttribute: React.FC<Props> = ({ export const CustomRelationAttribute: React.FC<Props> = ({
attributeDetails, attributeDetails,
className = "",
onChange, onChange,
projectId, projectId,
value, value,
@ -54,17 +58,30 @@ export const CustomRelationAttribute: React.FC<Props> = ({
: null : null
); );
const { members } = useProjectMembers(workspaceSlug?.toString(), projectId);
const optionsList = const optionsList =
attributeDetails.unit === "cycle" attributeDetails.unit === "cycle"
? cycles?.map((c) => ({ id: c.id, name: c.name })) ? cycles?.map((c) => ({ id: c.id, query: c.name, label: c.name }))
: attributeDetails.unit === "module" : attributeDetails.unit === "module"
? modules?.map((m) => ({ id: m.id, name: m.name })) ? modules?.map((m) => ({ id: m.id, query: m.name, label: m.name }))
: attributeDetails.unit === "user"
? members?.map((m) => ({
id: m.member.id,
query: m.member.display_name,
label: (
<div className="flex items-center gap-2">
<Avatar user={m.member} />
{m.member.is_bot ? m.member.first_name : m.member.display_name}
</div>
),
}))
: []; : [];
const selectedOption = (optionsList ?? []).find((option) => option.id === value); const selectedOption = (optionsList ?? []).find((option) => option.id === value);
const options = (optionsList ?? []).filter((option) => const options = (optionsList ?? []).filter((option) =>
option.name.toLowerCase().includes(query.toLowerCase()) option.query.toLowerCase().includes(query.toLowerCase())
); );
return ( return (
@ -76,8 +93,10 @@ export const CustomRelationAttribute: React.FC<Props> = ({
> >
{({ open }: { open: boolean }) => ( {({ open }: { open: boolean }) => (
<> <>
<Combobox.Button className="flex items-center text-xs rounded px-2.5 py-0.5 truncate w-min max-w-full text-left bg-custom-background-80"> <Combobox.Button
{selectedOption?.name ?? `Select ${attributeDetails.unit}`} className={`lex items-center text-xs rounded px-2.5 py-0.5 truncate w-min max-w-full text-left bg-custom-background-80 ${className}`}
>
{selectedOption?.label ?? `Select ${attributeDetails.unit}`}
</Combobox.Button> </Combobox.Button>
<Transition <Transition
show={open} show={open}
@ -109,7 +128,7 @@ export const CustomRelationAttribute: React.FC<Props> = ({
value={option.id} value={option.id}
className="flex items-center gap-1 cursor-pointer select-none truncate rounded px-1 py-1.5 hover:bg-custom-background-80 w-full" className="flex items-center gap-1 cursor-pointer select-none truncate rounded px-1 py-1.5 hover:bg-custom-background-80 w-full"
> >
<span className="px-1 rounded-sm truncate">{option.name}</span> {option.label}
</Combobox.Option> </Combobox.Option>
)) ))
) : ( ) : (

View File

@ -11,6 +11,7 @@ import { ICustomAttribute } from "types";
type Props = { type Props = {
attributeDetails: ICustomAttribute; attributeDetails: ICustomAttribute;
className?: string;
issueId: string; issueId: string;
onChange: (val: string | string[] | undefined) => void; onChange: (val: string | string[] | undefined) => void;
projectId: string; projectId: string;
@ -23,7 +24,7 @@ type Props = {
); );
export const CustomSelectAttribute: React.FC<Props> = (props) => { export const CustomSelectAttribute: React.FC<Props> = (props) => {
const { attributeDetails, multiple = false, onChange, value } = props; const { attributeDetails, className = "", multiple = false, onChange, value } = props;
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [query, setQuery] = useState(""); const [query, setQuery] = useState("");
@ -31,10 +32,6 @@ export const CustomSelectAttribute: React.FC<Props> = (props) => {
const dropdownButtonRef = useRef<HTMLButtonElement>(null); const dropdownButtonRef = useRef<HTMLButtonElement>(null);
const dropdownOptionsRef = useRef<HTMLUListElement>(null); const dropdownOptionsRef = useRef<HTMLUListElement>(null);
const selectedOption =
attributeDetails.children.find((option) => option.id === value) ??
attributeDetails.children.find((option) => option.is_default);
const options = attributeDetails.children.filter((option) => const options = attributeDetails.children.filter((option) =>
option.display_name.toLowerCase().includes(query.toLowerCase()) option.display_name.toLowerCase().includes(query.toLowerCase())
); );
@ -118,7 +115,9 @@ export const CustomSelectAttribute: React.FC<Props> = (props) => {
})} })}
</div> </div>
) : ( ) : (
<div className="text-xs px-2.5 py-0.5 rounded bg-custom-background-80"> <div
className={`text-xs px-2.5 py-0.5 rounded bg-custom-background-80 ${className}`}
>
Select {attributeDetails.display_name} Select {attributeDetails.display_name}
</div> </div>
) )
@ -148,7 +147,9 @@ export const CustomSelectAttribute: React.FC<Props> = (props) => {
</div> </div>
) )
) : ( ) : (
<div className="cursor-pointer text-xs truncate bg-custom-background-80 px-2.5 py-0.5 rounded"> <div
className={`cursor-pointer text-xs truncate bg-custom-background-80 px-2.5 py-0.5 rounded ${className}`}
>
Select {attributeDetails.display_name} Select {attributeDetails.display_name}
</div> </div>
)} )}

View File

@ -28,15 +28,11 @@ export const RelationAttributeForm: React.FC<FormComponentProps> = ({ control })
optionsClassName="w-full" optionsClassName="w-full"
input input
> >
{CUSTOM_ATTRIBUTE_UNITS.map((unit) => { {CUSTOM_ATTRIBUTE_UNITS.map((unit) => (
if (unit.value === "user") return null;
return (
<CustomSelect.Option key={unit.value} value={unit.value}> <CustomSelect.Option key={unit.value} value={unit.value}>
{unit.label} {unit.label}
</CustomSelect.Option> </CustomSelect.Option>
); ))}
})}
</CustomSelect> </CustomSelect>
)} )}
/> />

View File

@ -77,7 +77,7 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer((props)
<div className="flex items-center justify-between gap-2"> <div className="flex items-center justify-between gap-2">
<Disclosure.Button className="font-medium flex items-center gap-2"> <Disclosure.Button className="font-medium flex items-center gap-2">
<ChevronDown <ChevronDown
className={`transition-all ${open ? "-rotate-90" : ""}`} className={`transition-all ${open ? "" : "-rotate-90"}`}
size={14} size={14}
strokeWidth={1.5} strokeWidth={1.5}
/> />
@ -119,7 +119,7 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer((props)
</> </>
)} )}
</Disclosure> </Disclosure>
<div className="flex items-center gap-1 flex-wrap mt-3.5"> <div className="flex items-center gap-2 flex-wrap mt-3.5">
{Object.entries(nonDescriptionFields).map(([attributeId, attribute]) => ( {Object.entries(nonDescriptionFields).map(([attributeId, attribute]) => (
<div key={attributeId}> <div key={attributeId}>
{attribute.type === "checkbox" && ( {attribute.type === "checkbox" && (
@ -134,6 +134,7 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer((props)
{attribute.type === "datetime" && ( {attribute.type === "datetime" && (
<CustomDateTimeAttribute <CustomDateTimeAttribute
attributeDetails={attribute} attributeDetails={attribute}
className="bg-transparent border border-custom-border-200 py-1"
issueId={issueId} issueId={issueId}
onChange={(val) => onChange={(val) =>
onChange(attribute.id, val ? [val.toISOString()] : undefined) onChange(attribute.id, val ? [val.toISOString()] : undefined)
@ -147,15 +148,17 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer((props)
{attribute.type === "file" && ( {attribute.type === "file" && (
<CustomFileAttribute <CustomFileAttribute
attributeDetails={attribute} attributeDetails={attribute}
className="bg-transparent border border-custom-border-200 py-1"
issueId={issueId} issueId={issueId}
onChange={(val) => onChange(attribute.id, val)} onChange={(val) => onChange(attribute.id, val)}
projectId={projectId} projectId={projectId}
value={undefined} value={values[attribute.id]?.[0]}
/> />
)} )}
{attribute.type === "multi_select" && ( {attribute.type === "multi_select" && (
<CustomSelectAttribute <CustomSelectAttribute
attributeDetails={attribute} attributeDetails={attribute}
className="bg-transparent border border-custom-border-200 py-1"
issueId={issueId} issueId={issueId}
onChange={(val) => onChange(attribute.id, val)} onChange={(val) => onChange(attribute.id, val)}
projectId={projectId} projectId={projectId}
@ -166,6 +169,7 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer((props)
{attribute.type === "relation" && ( {attribute.type === "relation" && (
<CustomRelationAttribute <CustomRelationAttribute
attributeDetails={attribute} attributeDetails={attribute}
className="bg-transparent border border-custom-border-200 py-1"
issueId={issueId} issueId={issueId}
onChange={(val) => onChange(attribute.id, val)} onChange={(val) => onChange(attribute.id, val)}
projectId={projectId} projectId={projectId}
@ -175,6 +179,7 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer((props)
{attribute.type === "select" && ( {attribute.type === "select" && (
<CustomSelectAttribute <CustomSelectAttribute
attributeDetails={attribute} attributeDetails={attribute}
className="bg-transparent border border-custom-border-200 py-1"
issueId={issueId} issueId={issueId}
onChange={(val) => onChange(attribute.id, val)} onChange={(val) => onChange(attribute.id, val)}
projectId={projectId} projectId={projectId}

View File

@ -42,17 +42,14 @@ export const ObjectsSelect: React.FC<Props> = observer(({ onChange, projectId, v
return ( return (
<CustomSearchSelect <CustomSearchSelect
customButton={ label={entities?.find((e) => e.id === value)?.display_name ?? "Default"}
<button type="button" className="bg-custom-background-80 rounded text-xs px-2.5 py-0.5">
{entities?.find((e) => e.id === value)?.display_name ?? "Default"}
</button>
}
value={value} value={value}
maxHeight="md" maxHeight="md"
optionsClassName="!min-w-[10rem]" optionsClassName="!min-w-[10rem]"
onChange={onChange} onChange={onChange}
options={options} options={options}
position="right" position="right"
noChevron
/> />
); );
}); });

View File

@ -49,7 +49,7 @@ export const IssueAssigneeSelect: React.FC<Props> = ({ projectId, value = [], on
<AssigneesList userIds={value} length={3} showLength={true} /> <AssigneesList userIds={value} length={3} showLength={true} />
</div> </div>
) : ( ) : (
<div className="flex items-center justify-center gap-2 px-1.5 py-1 rounded shadow-sm border border-custom-border-300 hover:bg-custom-background-80"> <div className="flex items-center justify-center gap-2 px-1.5 py-1 rounded shadow-sm border border-custom-border-200 hover:bg-custom-background-80">
<Icon iconName="person" className="!text-base !leading-4" /> <Icon iconName="person" className="!text-base !leading-4" />
<span className="text-custom-text-200">Assignee</span> <span className="text-custom-text-200">Assignee</span>
</div> </div>

View File

@ -19,7 +19,7 @@ export const IssueDateSelect: React.FC<Props> = ({ label, maxDate, minDate, onCh
<Popover className="relative flex items-center justify-center rounded-lg"> <Popover className="relative flex items-center justify-center rounded-lg">
{({ close }) => ( {({ close }) => (
<> <>
<Popover.Button className="flex cursor-pointer items-center rounded-md border border-custom-border-200 text-xs shadow-sm duration-200"> <Popover.Button className="flex cursor-pointer items-center rounded border border-custom-border-200 text-xs shadow-sm duration-200">
<span className="flex items-center justify-center gap-2 px-2 py-1 text-xs text-custom-text-200 hover:bg-custom-background-80"> <span className="flex items-center justify-center gap-2 px-2 py-1 text-xs text-custom-text-200 hover:bg-custom-background-80">
{value ? ( {value ? (
<> <>

View File

@ -69,7 +69,7 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
/> />
</span> </span>
) : ( ) : (
<span className="flex items-center justify-center gap-2 px-2 py-1 text-xs rounded shadow-sm border border-custom-border-300 hover:bg-custom-background-80"> <span className="flex items-center justify-center gap-2 px-2 py-1 text-xs rounded shadow-sm border border-custom-border-200 hover:bg-custom-background-80">
<TagIcon className="h-3.5 w-3.5 text-custom-text-200" /> <TagIcon className="h-3.5 w-3.5 text-custom-text-200" />
<span className=" text-custom-text-200">Label</span> <span className=" text-custom-text-200">Label</span>
</span> </span>

View File

@ -78,7 +78,7 @@ export const CustomSearchSelect = ({
) : ( ) : (
<Combobox.Button <Combobox.Button
type="button" type="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 ${ className={`flex items-center justify-between gap-1 w-full rounded shadow-sm border border-custom-border-200 duration-300 focus:outline-none ${
input ? "px-3 py-2 text-sm" : "px-2.5 py-1 text-xs" input ? "px-3 py-2 text-sm" : "px-2.5 py-1 text-xs"
} ${ } ${
disabled disabled

View File

@ -45,7 +45,7 @@ const CustomSelect = ({
) : ( ) : (
<Listbox.Button <Listbox.Button
type="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 ${ className={`flex items-center justify-between gap-1 w-full rounded border border-custom-border-200 shadow-sm duration-300 focus:outline-none ${
input ? "px-3 py-2 text-sm" : "px-2.5 py-1 text-xs" input ? "px-3 py-2 text-sm" : "px-2.5 py-1 text-xs"
} ${ } ${
disabled disabled