diff --git a/plane-src/apps/web/ce/components/projects/external-contours/create-modal.tsx b/plane-src/apps/web/ce/components/projects/external-contours/create-modal.tsx index ff4333c..da966de 100644 --- a/plane-src/apps/web/ce/components/projects/external-contours/create-modal.tsx +++ b/plane-src/apps/web/ce/components/projects/external-contours/create-modal.tsx @@ -22,7 +22,7 @@ export function ExternalContourCreateModalRoot(props: Props) { isOpen={modalState} position={EModalPosition.CENTER} width={EModalWidth.XXXXL} - className="rounded-lg !bg-transparent shadow-none transition-[width] ease-linear" + className="transition-[width] ease-linear" > diff --git a/plane-src/apps/web/ce/components/projects/external-contours/create-properties.tsx b/plane-src/apps/web/ce/components/projects/external-contours/create-properties.tsx index 9c19cef..2dbc9a1 100644 --- a/plane-src/apps/web/ce/components/projects/external-contours/create-properties.tsx +++ b/plane-src/apps/web/ce/components/projects/external-contours/create-properties.tsx @@ -4,13 +4,13 @@ * See the LICENSE file for details. */ -import { useMemo } from "react"; +import { CalendarDays, SignalHigh } from "lucide-react"; import { observer } from "mobx-react"; +import { ISSUE_PRIORITIES } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; -import { Badge } from "@plane/propel/badge"; -import { MembersPropertyIcon } from "@plane/propel/icons"; +import { LabelPropertyIcon, MembersPropertyIcon, PriorityIcon, ProjectIcon } from "@plane/propel/icons"; import type { TIssue, TIssuePriorities } from "@plane/types"; -import { renderFormattedPayloadDate } from "@plane/utils"; +import { cn, renderFormattedDate, renderFormattedPayloadDate } from "@plane/utils"; import { DateDropdown } from "@/components/dropdowns/date"; import { ButtonAvatars } from "@/components/dropdowns/member/avatar"; import { MemberDropdownBase } from "@/components/dropdowns/member/base"; @@ -31,30 +31,31 @@ export const ExternalContoursCreateProperties = observer(function ExternalContou const { t } = useTranslation(); const { getUserDetails } = useMember(); const { targetProjectIds, getTargetOptionsByProjectId, getTargetProjectById } = useProjectExternalContours(); + const controlClassName = + "nodedc-modal-field flex h-10 min-w-fit items-center gap-2 rounded-[1.25rem] px-3.5 text-[12px] font-medium text-secondary"; const selectedTargetProject = data.target_project_id ? getTargetProjectById(data.target_project_id) : undefined; const selectedTargetOptions = getTargetOptionsByProjectId(data.target_project_id); - const targetLabelIds = useMemo( - () => selectedTargetOptions?.labels?.map((label) => label.id) ?? [], - [selectedTargetOptions?.labels] - ); - const assigneeLabel = useMemo(() => { - const assigneeIds = data.assignee_ids || []; - if (!assigneeIds.length) return t("external_contours_page.form.assignee"); - if (assigneeIds.length === 1) return getUserDetails(assigneeIds[0])?.display_name || t("external_contours_page.form.assignee"); - return `${assigneeIds.length} ${t("assignees").toLocaleLowerCase()}`; - }, [data.assignee_ids, getUserDetails, t]); const getTargetLabelById = (labelId: string) => selectedTargetOptions?.labels?.find((label) => label.id === labelId) ?? null; + const targetLabelIds = selectedTargetOptions?.labels?.map((label) => label.id) ?? []; + const selectedLabels = (data.label_ids || []).map((labelId) => getTargetLabelById(labelId)).filter((label) => !!label); + const assigneeIds = data.assignee_ids || []; + const assigneeLabel = !assigneeIds.length + ? t("external_contours_page.form.assignee") + : assigneeIds.length === 1 + ? (getUserDetails(assigneeIds[0])?.display_name ?? t("external_contours_page.form.assignee")) + : `${assigneeIds.length} ${t("assignees").toLocaleLowerCase()}`; + const priorityDetails = ISSUE_PRIORITIES.find((priority) => priority.key === data.priority); return ( -
-
- {t("external_contours_page.form.source_project")} - {currentProjectName || "NODE.DC"} +
+
+ {t("external_contours_page.form.source_project")} + {currentProjectName || "NODE.DC"}
-
+
{ @@ -67,41 +68,36 @@ export const ExternalContoursCreateProperties = observer(function ExternalContou multiple={false} projectIds={targetProjectIds} getProjectById={getTargetProjectById} - buttonVariant="border-with-text" + buttonVariant="transparent-without-text" + buttonContainerClassName="h-full" + button={ +
+ + + {selectedTargetProject?.name ?? t("external_contours_page.form.target_project")} + +
+ } placeholder={t("external_contours_page.form.target_project")} disabled={targetProjectIds.length === 0} />
- {selectedTargetProject && ( -
- {t("external_contours_page.form.selected_target")} - {selectedTargetProject.name} -
- )} - -
- handleData("priority", priority)} - buttonVariant="border-with-text" - /> -
- -
+
handleData("assignee_ids", assigneeIds)} + value={assigneeIds} + onChange={(nextAssigneeIds) => handleData("assignee_ids", nextAssigneeIds)} getUserDetails={getUserDetails} memberIds={selectedTargetOptions?.member_ids ?? []} button={ -
- - {assigneeLabel} +
+ + 0 ? "text-primary" : "text-tertiary")}> + {assigneeLabel} +
} - buttonVariant={(data.assignee_ids || []).length > 0 ? "transparent-without-text" : "border-with-text"} - buttonClassName={(data.assignee_ids || []).length > 0 ? "hover:bg-transparent" : ""} + buttonVariant="transparent-without-text" optionsClassName="z-[60]" placeholder={t("external_contours_page.form.assignee")} disabled={!data.target_project_id || !selectedTargetOptions} @@ -109,21 +105,67 @@ export const ExternalContoursCreateProperties = observer(function ExternalContou />
-
+
+ handleData("priority", priority)} + buttonVariant="transparent-without-text" + buttonContainerClassName="h-full" + button={ +
+ {data.priority && data.priority !== "none" ? ( + + ) : ( + + )} + + {data.priority && data.priority !== "none" + ? priorityDetails?.title + : t("external_contours_page.form.priority")} + +
+ } + placeholder={t("external_contours_page.form.priority")} + /> +
+ +
handleData("label_ids", labelIds)} getLabelById={getTargetLabelById} labelIds={targetLabelIds} + buttonContainerClassName="h-full !text-[12px]" + label={ +
+ + 0 ? "text-primary" : "text-tertiary")}> + {selectedLabels.length > 0 + ? selectedLabels.length === 1 + ? selectedLabels[0]?.name + : `${selectedLabels.length} ${t("labels").toLocaleLowerCase()}` + : t("labels")} + +
+ } disabled={!data.target_project_id || !selectedTargetOptions} />
-
+
handleData("target_date", date ? renderFormattedPayloadDate(date) : "")} - buttonVariant="border-with-text" + buttonVariant="transparent-without-text" + buttonContainerClassName="h-full" + button={ +
+ + + {data.target_date ? renderFormattedDate(data.target_date) : t("external_contours_page.form.due_date")} + +
+ } placeholder={t("external_contours_page.form.due_date")} />
diff --git a/plane-src/apps/web/ce/components/projects/external-contours/create-root.tsx b/plane-src/apps/web/ce/components/projects/external-contours/create-root.tsx index d940536..b725465 100644 --- a/plane-src/apps/web/ce/components/projects/external-contours/create-root.tsx +++ b/plane-src/apps/web/ce/components/projects/external-contours/create-root.tsx @@ -126,57 +126,67 @@ export const ExternalContoursCreateRoot = observer(function ExternalContoursCrea if (!workspaceSlug || !projectId || !workspaceId) return <>; return ( -
-
-
-
-
-

{t("external_contours_page.modal.title")}

-
-
- - {}} - /> - -
-
-
-
setCreateMore((prevData) => !prevData)} - role="button" - tabIndex={0} - > - {}} size="sm" /> - {t("create_more")} -
-
- - -
-
-
+
+
+
+

{t("external_contours_page.modal.title")}

+
+
+ + {}} + /> + +
-
+
+
setCreateMore((prevData) => !prevData)} + role="button" + tabIndex={0} + > + {}} size="sm" /> + {t("create_more")} +
+
+ + +
+
+ ); }); diff --git a/plane-src/apps/web/core/components/inbox/modals/create-modal/issue-title.tsx b/plane-src/apps/web/core/components/inbox/modals/create-modal/issue-title.tsx index 49d8178..0ae574e 100644 --- a/plane-src/apps/web/core/components/inbox/modals/create-modal/issue-title.tsx +++ b/plane-src/apps/web/core/components/inbox/modals/create-modal/issue-title.tsx @@ -11,7 +11,7 @@ import { useTranslation } from "@plane/i18n"; import type { TIssue } from "@plane/types"; import { Input } from "@plane/ui"; // helpers -import { getTabIndex } from "@plane/utils"; +import { cn, getTabIndex } from "@plane/utils"; // hooks import { usePlatformOS } from "@/hooks/use-platform-os"; @@ -19,10 +19,11 @@ type TInboxIssueTitle = { data: Partial; handleData: (issueKey: keyof Partial, issueValue: Partial[keyof Partial]) => void; isTitleLengthMoreThan255Character?: boolean; + inputClassName?: string; }; export const InboxIssueTitle = observer(function InboxIssueTitle(props: TInboxIssueTitle) { - const { data, handleData, isTitleLengthMoreThan255Character } = props; + const { data, handleData, isTitleLengthMoreThan255Character, inputClassName } = props; // hooks const { isMobile } = usePlatformOS(); @@ -37,7 +38,7 @@ export const InboxIssueTitle = observer(function InboxIssueTitle(props: TInboxIs value={data?.name} onChange={(e) => handleData("name", e.target.value)} placeholder={t("title")} - className="w-full text-14" + className={cn("w-full text-14", inputClassName)} tabIndex={getIndex("name")} required /> diff --git a/plane-src/apps/web/core/components/navigation/top-nav-power-k.tsx b/plane-src/apps/web/core/components/navigation/top-nav-power-k.tsx index c741a3f..bc090b0 100644 --- a/plane-src/apps/web/core/components/navigation/top-nav-power-k.tsx +++ b/plane-src/apps/web/core/components/navigation/top-nav-power-k.tsx @@ -130,7 +130,7 @@ export const TopNavPowerK = observer((props: TTopNavPowerKProps) => { const width = 320; const viewportPadding = 16; const left = Math.min(rect.left, window.innerWidth - width - viewportPadding); - const top = rect.top; + const top = rect.top + rect.height / 2; setSidebarSearchPosition({ left, @@ -372,6 +372,7 @@ export const TopNavPowerK = observer((props: TTopNavPowerKProps) => { left: `${sidebarSearchPosition.left}px`, top: `${sidebarSearchPosition.top}px`, width: `${sidebarSearchPosition.width}px`, + transform: "translateY(-50%)", }} >
diff --git a/plane-src/apps/web/core/components/power-k/ui/modal/shortcuts-root.tsx b/plane-src/apps/web/core/components/power-k/ui/modal/shortcuts-root.tsx index 50b0e30..1b37831 100644 --- a/plane-src/apps/web/core/components/power-k/ui/modal/shortcuts-root.tsx +++ b/plane-src/apps/web/core/components/power-k/ui/modal/shortcuts-root.tsx @@ -38,7 +38,7 @@ export function ShortcutsModal(props: Props) {
{/* Backdrop */}
; + onSidebarDropdownToggle: (value: boolean) => void; + onSidebarPanelPositionChange: (position: { left: number; top: number; width: number } | null) => void; +}; + +function WorkspaceMenuStateSync(props: WorkspaceMenuStateSyncProps) { + const { open, variant, sidebarPanelButtonRef, onSidebarDropdownToggle, onSidebarPanelPositionChange } = props; + + const updateSidebarPanelMenuPosition = useCallback(() => { + if (variant !== "sidebar-panel" || !sidebarPanelButtonRef.current || typeof window === "undefined") return; + + const rect = sidebarPanelButtonRef.current.getBoundingClientRect(); + const width = 480; + const viewportPadding = 16; + + onSidebarPanelPositionChange({ + left: Math.min(rect.left, window.innerWidth - width - viewportPadding), + top: rect.bottom + 8, + width, + }); + }, [onSidebarPanelPositionChange, sidebarPanelButtonRef, variant]); + + useEffect(() => { + onSidebarDropdownToggle(open); + }, [onSidebarDropdownToggle, open]); + + useLayoutEffect(() => { + if (!open || variant !== "sidebar-panel") { + onSidebarPanelPositionChange(null); + return; + } + + updateSidebarPanelMenuPosition(); + + const handlePositionUpdate = () => updateSidebarPanelMenuPosition(); + window.addEventListener("resize", handlePositionUpdate); + window.addEventListener("scroll", handlePositionUpdate, true); + + return () => { + window.removeEventListener("resize", handlePositionUpdate); + window.removeEventListener("scroll", handlePositionUpdate, true); + }; + }, [onSidebarPanelPositionChange, open, updateSidebarPanelMenuPosition, variant]); + + return null; +} + export const WorkspaceMenuRoot = observer(function WorkspaceMenuRoot(props: WorkspaceMenuRootProps) { const { variant } = props; // store hooks @@ -48,7 +98,6 @@ export const WorkspaceMenuRoot = observer(function WorkspaceMenuRoot(props: Work // translation const { t } = useTranslation(); // local state - const [isWorkspaceMenuOpen, setIsWorkspaceMenuOpen] = useState(false); const [sidebarPanelMenuPosition, setSidebarPanelMenuPosition] = useState<{ left: number; top: number; @@ -77,40 +126,6 @@ export const WorkspaceMenuRoot = observer(function WorkspaceMenuRoot(props: Work const workspacesList = orderWorkspacesList(Object.values(workspaces ?? {})); // TODO: fix workspaces list scroll - const updateSidebarPanelMenuPosition = useCallback(() => { - if (variant !== "sidebar-panel" || !sidebarPanelButtonRef.current || typeof window === "undefined") return; - - const rect = sidebarPanelButtonRef.current.getBoundingClientRect(); - const width = 480; - const viewportPadding = 16; - - setSidebarPanelMenuPosition({ - left: Math.min(rect.left, window.innerWidth - width - viewportPadding), - top: rect.bottom + 8, - width, - }); - }, [variant]); - - // Toggle sidebar dropdown state when either menu is open - useEffect(() => { - toggleAnySidebarDropdown(isWorkspaceMenuOpen); - }, [isWorkspaceMenuOpen, toggleAnySidebarDropdown]); - - useLayoutEffect(() => { - if (!isWorkspaceMenuOpen || variant !== "sidebar-panel") return; - - updateSidebarPanelMenuPosition(); - - const handlePositionUpdate = () => updateSidebarPanelMenuPosition(); - window.addEventListener("resize", handlePositionUpdate); - window.addEventListener("scroll", handlePositionUpdate, true); - - return () => { - window.removeEventListener("resize", handlePositionUpdate); - window.removeEventListener("scroll", handlePositionUpdate, true); - }; - }, [isWorkspaceMenuOpen, updateSidebarPanelMenuPosition, variant]); - return ( {({ open, close }: { open: boolean; close: () => void }) => { - // Update local state directly - if (isWorkspaceMenuOpen !== open) { - setIsWorkspaceMenuOpen(open); - } - return ( <> + {variant === "sidebar" && (