Skip to content

Commit

Permalink
chore: components restructuring and UI improvements. (makeplane#5285)
Browse files Browse the repository at this point in the history
* chore: components restructuring and minor UI improvements.

* chore: minor UI improvements fro icons and member dropdown.

* chore: update issue identifier.

* chore: rename `Issue Extra Property` to `Issue Additional Property`

* chore: fix popovers placement issue on components with overflow.

* chore: add `scrollbar-xs`

* chore: add `xs` size for input and textarea components.

* chore: update `sortable` to return back `movedItem` in the onChange callback.

* chore: minor UI adjustments for radio-select.

* chore: update outside click delay to 1ms.
  • Loading branch information
prateekshourya29 authored Aug 5, 2024
1 parent 07574b4 commit 333a989
Show file tree
Hide file tree
Showing 67 changed files with 823 additions and 568 deletions.
9 changes: 7 additions & 2 deletions packages/types/src/issues/base.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ export * from "./issue_relation";
export * from "./issue_sub_issues";
export * from "./activity/base";

export type TLoader = "init-loader" | "mutation" | "pagination" | undefined;
export type TLoader =
| "init-loader"
| "mutation"
| "pagination"
| "loaded"
| undefined;

export type TGroupedIssues = {
[group_id: string]: string[];
Expand All @@ -36,4 +41,4 @@ export type TGroupedIssueCount = {
[group_id: string]: number;
};

export type TUnGroupedIssues = string[];
export type TUnGroupedIssues = string[];
3 changes: 3 additions & 0 deletions packages/types/src/issues/issue.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type TBaseIssue = {
parent_id: string | null;
cycle_id: string | null;
module_ids: string[] | null;
type_id: string | null;

created_at: string;
updated_at: string;
Expand All @@ -48,6 +49,8 @@ export type TIssue = TBaseIssue & {
issue_link?: TIssueLink[];
// tempId is used for optimistic updates. It is not a part of the API response.
tempId?: string;
// sourceIssueId is used to store the original issue id when creating a copy of an issue. Used in cloning property values. It is not a part of the API response.
sourceIssueId?: string;
};

export type TIssueMap = {
Expand Down
2 changes: 2 additions & 0 deletions packages/types/src/project/projects.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface IProject {
identifier: string;
anchor: string | null;
is_favorite: boolean;
is_issue_type_enabled: boolean;
is_member: boolean;
is_time_tracking_enabled: boolean;
logo_props: TLogoProps;
Expand All @@ -58,6 +59,7 @@ export interface IProjectLite {
id: string;
name: string;
identifier: string;
logo_props: TLogoProps;
}

type ProjectPreferences = {
Expand Down
5 changes: 3 additions & 2 deletions packages/ui/src/collapsible/collapsible.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Disclosure, Transition } from "@headlessui/react";
export type TCollapsibleProps = {
title: string | React.ReactNode;
children: React.ReactNode;
buttonRef?: React.RefObject<HTMLButtonElement>;
className?: string;
buttonClassName?: string;
isOpen?: boolean;
Expand All @@ -12,7 +13,7 @@ export type TCollapsibleProps = {
};

export const Collapsible: FC<TCollapsibleProps> = (props) => {
const { title, children, className, buttonClassName, isOpen, onToggle, defaultOpen } = props;
const { title, children, buttonRef, className, buttonClassName, isOpen, onToggle, defaultOpen } = props;
// state
const [localIsOpen, setLocalIsOpen] = useState<boolean>(isOpen || defaultOpen ? true : false);

Expand All @@ -33,7 +34,7 @@ export const Collapsible: FC<TCollapsibleProps> = (props) => {

return (
<Disclosure as="div" className={className}>
<Disclosure.Button className={buttonClassName} onClick={handleOnClick}>
<Disclosure.Button ref={buttonRef} className={buttonClassName} onClick={handleOnClick}>
{title}
</Disclosure.Button>
<Transition
Expand Down
151 changes: 81 additions & 70 deletions packages/ui/src/dropdowns/custom-search-select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useRef, useState } from "react";
import { usePopper } from "react-popper";
import { Combobox } from "@headlessui/react";
import { Check, ChevronDown, Search } from "lucide-react";
import { createPortal } from "react-dom";
// hooks
import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down";
import useOutsideClickDetector from "../hooks/use-outside-click-detector";
Expand All @@ -15,6 +16,7 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => {
customButtonClassName = "",
buttonClassName = "",
className = "",
chevronClassName = "",
customButton,
placement,
disabled = false,
Expand Down Expand Up @@ -59,10 +61,12 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => {
setIsOpen(true);
if (referenceElement) referenceElement.focus();
};

const closeDropdown = () => {
setIsOpen(false);
onClose && onClose();
};

const handleKeyDown = useDropdownKeyDown(openDropdown, closeDropdown, isOpen);
useOutsideClickDetector(dropdownRef, closeDropdown);

Expand Down Expand Up @@ -105,86 +109,93 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => {
<button
ref={setReferenceElement}
type="button"
className={`flex w-full items-center justify-between gap-1 rounded border-[0.5px] border-custom-border-300 ${
input ? "px-3 py-2 text-sm" : "px-2 py-1 text-xs"
} ${
disabled
? "cursor-not-allowed text-custom-text-200"
: "cursor-pointer hover:bg-custom-background-80"
} ${buttonClassName}`}
className={cn(
"flex w-full items-center justify-between gap-1 rounded border-[0.5px] border-custom-border-300",
{
"px-3 py-2 text-sm": input,
"px-2 py-1 text-xs": !input,
"cursor-not-allowed text-custom-text-200": disabled,
"cursor-pointer hover:bg-custom-background-80": !disabled,
},
buttonClassName
)}
onClick={toggleDropdown}
>
{label}
{!noChevron && !disabled && <ChevronDown className="h-3 w-3 flex-shrink-0" aria-hidden="true" />}
{!noChevron && !disabled && (
<ChevronDown className={cn("h-3 w-3 flex-shrink-0", chevronClassName)} aria-hidden="true" />
)}
</button>
</Combobox.Button>
)}
{isOpen && (
<Combobox.Options className="fixed z-10" static>
<div
className={cn(
"my-1 overflow-y-scroll rounded-md border-[0.5px] border-custom-border-300 bg-custom-background-100 px-2 py-2.5 text-xs shadow-custom-shadow-rg focus:outline-none min-w-[12rem] whitespace-nowrap",
optionsClassName
)}
ref={setPopperElement}
style={styles.popper}
{...attributes.popper}
>
<div className="flex items-center gap-1.5 rounded border border-custom-border-100 bg-custom-background-90 px-2">
<Search className="h-3.5 w-3.5 text-custom-text-400" strokeWidth={1.5} />
<Combobox.Input
className="w-full bg-transparent py-1 text-xs text-custom-text-200 placeholder:text-custom-text-400 focus:outline-none"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search"
displayValue={(assigned: any) => assigned?.name}
/>
</div>
{isOpen &&
createPortal(
<Combobox.Options data-prevent-outside-click static>
<div
className={cn("mt-2 space-y-1 overflow-y-scroll", {
"max-h-60": maxHeight === "lg",
"max-h-48": maxHeight === "md",
"max-h-36": maxHeight === "rg",
"max-h-28": maxHeight === "sm",
})}
className={cn(
"my-1 overflow-y-scroll rounded-md border-[0.5px] border-custom-border-300 bg-custom-background-100 px-2 py-2.5 text-xs shadow-custom-shadow-rg focus:outline-none min-w-48 whitespace-nowrap z-20",
optionsClassName
)}
ref={setPopperElement}
style={styles.popper}
{...attributes.popper}
>
{filteredOptions ? (
filteredOptions.length > 0 ? (
filteredOptions.map((option) => (
<Combobox.Option
key={option.value}
value={option.value}
className={({ active }) =>
cn(
"w-full truncate flex items-center justify-between gap-2 rounded px-1 py-1.5 cursor-pointer select-none",
{
"bg-custom-background-80": active,
}
)
}
onClick={() => {
if (!multiple) closeDropdown();
}}
>
{({ selected }) => (
<>
<span className="flex-grow truncate">{option.content}</span>
{selected && <Check className="h-3.5 w-3.5 flex-shrink-0" />}
</>
)}
</Combobox.Option>
))
<div className="flex items-center gap-1.5 rounded border border-custom-border-100 bg-custom-background-90 px-2">
<Search className="h-3.5 w-3.5 text-custom-text-400" strokeWidth={1.5} />
<Combobox.Input
className="w-full bg-transparent py-1 text-xs text-custom-text-200 placeholder:text-custom-text-400 focus:outline-none"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search"
displayValue={(assigned: any) => assigned?.name}
/>
</div>
<div
className={cn("mt-2 space-y-1 overflow-y-scroll", {
"max-h-60": maxHeight === "lg",
"max-h-48": maxHeight === "md",
"max-h-36": maxHeight === "rg",
"max-h-28": maxHeight === "sm",
})}
>
{filteredOptions ? (
filteredOptions.length > 0 ? (
filteredOptions.map((option) => (
<Combobox.Option
key={option.value}
value={option.value}
className={({ active }) =>
cn(
"w-full truncate flex items-center justify-between gap-2 rounded px-1 py-1.5 cursor-pointer select-none",
{
"bg-custom-background-80": active,
}
)
}
onClick={() => {
if (!multiple) closeDropdown();
}}
>
{({ selected }) => (
<>
<span className="flex-grow truncate">{option.content}</span>
{selected && <Check className="h-3.5 w-3.5 flex-shrink-0" />}
</>
)}
</Combobox.Option>
))
) : (
<p className="text-custom-text-400 italic py-1 px-1.5">No matches found</p>
)
) : (
<p className="text-custom-text-400 italic py-1 px-1.5">No matches found</p>
)
) : (
<p className="text-custom-text-400 italic py-1 px-1.5">Loading...</p>
)}
<p className="text-custom-text-400 italic py-1 px-1.5">Loading...</p>
)}
</div>
{footerOption}
</div>
{footerOption}
</div>
</Combobox.Options>
)}
</Combobox.Options>,
document.body
)}
</>
);
}}
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/dropdowns/helper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface IDropdownProps {
label?: string | JSX.Element;
maxHeight?: "sm" | "rg" | "md" | "lg";
noChevron?: boolean;
chevronClassName?: string;
onOpen?: () => void;
optionsClassName?: string;
placement?: Placement;
Expand Down
6 changes: 6 additions & 0 deletions packages/ui/src/emoji/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ import {
Minus,
MinusCircle,
MinusSquare,
CircleChevronDown,
UsersRound,
ToggleLeft,
} from "lucide-react";

export const MATERIAL_ICONS_LIST = [
Expand Down Expand Up @@ -791,6 +794,7 @@ export const LUCIDE_ICONS_LIST = [
{ name: "Camera", element: Camera },
{ name: "CameraOff", element: CameraOff },
{ name: "Cast", element: Cast },
{ name: "CircleChevronDown", element: CircleChevronDown },
{ name: "Check", element: Check },
{ name: "CheckCircle", element: CheckCircle },
{ name: "CheckSquare", element: CheckSquare },
Expand Down Expand Up @@ -908,4 +912,6 @@ export const LUCIDE_ICONS_LIST = [
{ name: "Minus", element: Minus },
{ name: "MinusCircle", element: MinusCircle },
{ name: "MinusSquare", element: MinusSquare },
{ name: "ToggleLeft", element: ToggleLeft },
{ name: "UsersRound", element: UsersRound },
];
11 changes: 7 additions & 4 deletions packages/ui/src/emoji/logo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import useFontFaceObserver from "use-font-face-observer";
import { LUCIDE_ICONS_LIST } from "./icons";
// helpers
import { emojiCodeToUnicode } from "./helpers";
import { cn } from "../../helpers";

type TLogoProps = {
in_use: "emoji" | "icon";
Expand All @@ -22,10 +23,11 @@ type Props = {
logo: TLogoProps;
size?: number;
type?: "lucide" | "material";
customColor?: string;
};

export const Logo: FC<Props> = (props) => {
const { logo, size = 16, type = "material" } = props;
const { logo, size = 16, customColor, type = "material" } = props;

// destructuring the logo object
const { in_use, emoji, icon } = logo;
Expand Down Expand Up @@ -72,19 +74,20 @@ export const Logo: FC<Props> = (props) => {
{lucideIcon && (
<lucideIcon.element
style={{
color: color,
color: !customColor ? color : undefined,
height: size,
width: size,
}}
className={cn(customColor)}
/>
)}
</>
) : (
<span
className="material-symbols-rounded"
className={cn("material-symbols-rounded", customColor)}
style={{
fontSize: size,
color: color,
color: !customColor ? color : undefined,
scale: "115%",
}}
>
Expand Down
3 changes: 2 additions & 1 deletion packages/ui/src/form-fields/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { cn } from "../../helpers";

export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
mode?: "primary" | "transparent" | "true-transparent";
inputSize?: "sm" | "md";
inputSize?: "xs" | "sm" | "md";
hasError?: boolean;
className?: string;
}
Expand All @@ -26,6 +26,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>((props, ref) => {
mode === "transparent",
"rounded border-none bg-transparent ring-0": mode === "true-transparent",
"border-red-500": hasError,
"px-1.5 py-1": inputSize === "xs",
"px-3 py-2": inputSize === "sm",
"p-3": inputSize === "md",
},
Expand Down
Loading

0 comments on commit 333a989

Please sign in to comment.