refactor(space): migrate from @plane/propel to @makeplane/propel

- Drop the @plane/propel workspace dependency from apps/space
- Buttons/tooltips/toast switched to @makeplane/propel APIs (label-based
  Button, label/side Tooltip, manager-based toast adapter mirroring admin)
- Simple icons renamed to @makeplane/propel/icons equivalents
- Domain icons (StateGroup/Priority/CycleGroup) and Plane brand assets
  vendored into apps/space/components/icons
- Emoji reactions reimplemented locally on frimousse + propel popover
  (apps/space/components/emoji); animated counter dropped
- Remove dead react-day-picker stylesheet import from space globals
- Clean stale @plane/propel comments in admin
This commit is contained in:
sriramveeraghanta
2026-08-31 20:00:55 +05:30
parent bc2fce4f1b
commit 7a898680bf
75 changed files with 1316 additions and 216 deletions

View File

@@ -8,8 +8,7 @@ import React from "react";
// helpers
import { cn } from "@plane/utils";
// Loading-placeholder skeleton. @makeplane/propel has no skeleton component yet;
// this matches the API the workspace @plane/propel skeleton exposed.
// Loading-placeholder skeleton. @makeplane/propel has no skeleton component yet, so it is kept local.
type SkeletonProps = {
children: React.ReactNode;

View File

@@ -10,8 +10,7 @@ import { IconButton } from "@makeplane/propel/components/icon-button";
import { Icon } from "@makeplane/propel/components/icon";
import { CloseOutline } from "@makeplane/propel/icons";
// Keep the call-site API the workspace @plane/propel toast exposed, backed by
// @makeplane/propel's toast manager, so consumers only change their import path.
// Thin wrapper over @makeplane/propel's toast manager to keep the call-site API stable.
export enum TOAST_TYPE {
SUCCESS = "success",
ERROR = "error",

View File

@@ -5,7 +5,7 @@
*/
// ui
import { Button } from "@plane/propel/button";
import { Button } from "@makeplane/propel/components/button";
function ErrorPage() {
const handleRetry = () => {
@@ -31,12 +31,7 @@ function ErrorPage() {
</p>
</div>
<div className="flex items-center justify-center gap-2">
<Button variant="primary" size="lg" onClick={handleRetry}>
Refresh
</Button>
{/* <Button variant="secondary" size="lg" onClick={() => {}}>
Sign out
</Button> */}
<Button variant="primary" size="lg" stretch="auto" onClick={handleRetry} label="Refresh" />
</div>
</div>
</div>

View File

@@ -5,7 +5,7 @@
*/
import { Info } from "lucide-react";
import { CloseIcon } from "@plane/propel/icons";
import { CloseOutline } from "@makeplane/propel/icons";
// helpers
import type { TAuthErrorInfo } from "@/helpers/authentication.helper";
@@ -28,7 +28,7 @@ export function AuthBanner(props: TAuthBanner) {
className="relative ml-auto flex h-6 w-6 cursor-pointer items-center justify-center rounded-xs text-accent-primary/80 transition-all hover:bg-accent-primary/20"
onClick={() => handleBannerData && handleBannerData(undefined)}
>
<CloseIcon className="h-4 w-4 flex-shrink-0" />
<CloseOutline className="h-4 w-4 flex-shrink-0" />
</div>
</div>
);

View File

@@ -10,10 +10,10 @@ import { observer } from "mobx-react";
// icons
import { CircleAlert, XCircle } from "lucide-react";
// types
import { Button } from "@plane/propel/button";
import { Button } from "@makeplane/propel/components/button";
import type { IEmailCheckData } from "@plane/types";
// ui
import { Input, Spinner } from "@plane/ui";
import { Input } from "@plane/ui";
// helpers
import { cn } from "@plane/utils";
import { checkEmailValidity } from "@/helpers/string.helper";
@@ -100,9 +100,15 @@ export const AuthEmailForm = observer(function AuthEmailForm(props: TAuthEmailFo
</p>
)}
</div>
<Button type="submit" variant="primary" className="w-full" size="xl" disabled={isButtonDisabled}>
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Continue"}
</Button>
<Button
type="submit"
variant="primary"
size="lg"
stretch="full"
disabled={isButtonDisabled}
loading={isSubmitting}
label="Continue"
/>
</form>
);
});

View File

@@ -8,10 +8,10 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
import { observer } from "mobx-react";
import { Eye, EyeOff, XCircle } from "lucide-react";
// plane imports
import { Button } from "@makeplane/propel/components/button";
import { API_BASE_URL, E_PASSWORD_STRENGTH } from "@plane/constants";
import { Button } from "@plane/propel/button";
import { AuthService } from "@plane/services";
import { Input, Spinner, PasswordStrengthIndicator } from "@plane/ui";
import { Input, PasswordStrengthIndicator } from "@plane/ui";
import { getPasswordStrength } from "@plane/utils";
// types
import { EAuthModes, EAuthSteps } from "@/types/auth";
@@ -215,31 +215,36 @@ export const AuthPasswordForm = observer(function AuthPasswordForm(props: Props)
<div className="space-y-2.5">
{mode === EAuthModes.SIGN_IN ? (
<>
<Button type="submit" variant="primary" className="w-full" size="xl" disabled={isButtonDisabled}>
{isSubmitting ? (
<Spinner height="20px" width="20px" />
) : isSMTPConfigured ? (
"Continue"
) : (
"Go to workspace"
)}
</Button>
<Button
type="submit"
variant="primary"
size="lg"
stretch="full"
disabled={isButtonDisabled}
loading={isSubmitting}
label={isSMTPConfigured ? "Continue" : "Go to workspace"}
/>
{isSMTPConfigured && (
<Button
type="button"
onClick={redirectToUniqueCodeSignIn}
variant="secondary"
className="w-full"
size="xl"
>
Sign in with unique code
</Button>
size="lg"
stretch="full"
label="Sign in with unique code"
/>
)}
</>
) : (
<Button type="submit" variant="primary" className="w-full" size="xl" disabled={isButtonDisabled}>
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Create account"}
</Button>
<Button
type="submit"
variant="primary"
size="lg"
stretch="full"
disabled={isButtonDisabled}
loading={isSubmitting}
label="Create account"
/>
)}
</div>
</form>

View File

@@ -7,10 +7,10 @@
import React, { useEffect, useState } from "react";
import { CircleCheck, XCircle } from "lucide-react";
// plane imports
import { Button } from "@makeplane/propel/components/button";
import { API_BASE_URL } from "@plane/constants";
import { Button } from "@plane/propel/button";
import { AuthService } from "@plane/services";
import { Input, Spinner } from "@plane/ui";
import { Input } from "@plane/ui";
// hooks
import useTimer from "@/hooks/use-timer";
// types
@@ -148,9 +148,15 @@ export function AuthUniqueCodeForm(props: TAuthUniqueCodeForm) {
</div>
<div className="space-y-2.5">
<Button type="submit" variant="primary" className="w-full" size="xl" disabled={isButtonDisabled}>
{isRequestingNewCode ? "Sending code" : isSubmitting ? <Spinner height="20px" width="20px" /> : "Continue"}
</Button>
<Button
type="submit"
variant="primary"
size="lg"
stretch="full"
disabled={isButtonDisabled}
loading={isSubmitting}
label={isRequestingNewCode ? "Sending code" : "Continue"}
/>
</div>
</form>
);

View File

@@ -5,11 +5,11 @@
*/
import { observer } from "mobx-react";
import { PlaneLockup } from "@plane/propel/icons";
// assets
import UserLoggedInImage from "@/app/assets/user-logged-in.svg?url";
// components
import { PoweredBy } from "@/components/common/powered-by";
import { PlaneLockup } from "@/components/icons";
import { UserAvatar } from "@/components/issues/navbar/user-avatar";
// hooks
import { useUser } from "@/hooks/store/use-user";

View File

@@ -5,8 +5,8 @@
*/
import { WEBSITE_URL } from "@plane/constants";
// assets
import { PlaneLogo } from "@plane/propel/icons";
// components
import { PlaneLogo } from "@/components/icons";
type TPoweredBy = {
disabled?: boolean;

View File

@@ -8,8 +8,8 @@ import React, { useEffect, useState, useCallback } from "react";
// plane imports
import { TOOLBAR_ITEMS } from "@plane/editor";
import type { ToolbarMenuItem, EditorRefApi } from "@plane/editor";
import { Button } from "@plane/propel/button";
import { Tooltip } from "@plane/propel/tooltip";
import { Button } from "@makeplane/propel/components/button";
import { Tooltip } from "@makeplane/propel/components/tooltip";
import { cn } from "@plane/utils";
type Props = {
@@ -68,15 +68,7 @@ export function IssueCommentToolbar(props: Props) {
const isItemActive = activeStates[item.renderKey];
return (
<Tooltip
key={item.renderKey}
tooltipContent={
<p className="flex flex-col gap-1 text-center text-11">
<span className="font-medium">{item.name}</span>
{item.shortcut && <kbd className="text-placeholder">{item.shortcut.join(" + ")}</kbd>}
</p>
}
>
<Tooltip key={item.renderKey} label={item.name} shortcut={item.shortcut?.join(" + ")}>
<button
type="button"
onClick={() => executeCommand(item)}
@@ -104,13 +96,14 @@ export function IssueCommentToolbar(props: Props) {
<div className="sticky right-1">
<Button
type="button"
label="Comment"
variant="primary"
size="md"
stretch="auto"
onClick={handleSubmit}
disabled={isCommentEmpty}
loading={isSubmitting}
>
Comment
</Button>
/>
</div>
)}
</div>

View File

@@ -0,0 +1,141 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
import { useCallback, useEffect, useMemo, useRef } from "react";
import { EmojiPicker } from "frimousse";
import { Popover, PopoverContent, PopoverTrigger } from "@makeplane/propel/components/popover";
// plane imports
import { cn } from "@plane/utils";
// local imports
import { emojiToString } from "./helpers";
type EmojiRootProps = {
onChange: (value: string) => void;
searchPlaceholder?: string;
};
function EmojiRoot(props: EmojiRootProps) {
const { onChange, searchPlaceholder = "Search" } = props;
const searchWrapperRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const inputElement = searchWrapperRef.current?.querySelector("input");
if (inputElement) {
inputElement.removeAttribute("disabled");
inputElement.focus();
}
}, []);
return (
<EmojiPicker.Root
data-slot="emoji-picker"
className="isolate flex h-full w-full flex-col rounded-md border-none p-2"
onEmojiSelect={(val) => onChange(val.emoji)}
>
<div className="sticky top-0 z-10 flex items-center bg-layer-2 px-1.5 py-2">
<div ref={searchWrapperRef} data-slot="emoji-picker-search-wrapper" className="flex-grow">
<EmojiPicker.Search
placeholder={searchPlaceholder}
className="block h-full w-full rounded-md border-[0.5px] border-subtle bg-transparent px-3 py-2 text-16 placeholder-(--text-color-placeholder) focus:border-accent-strong focus:outline-none"
/>
</div>
</div>
<EmojiPicker.Viewport data-slot="emoji-picker-content" className="relative flex-1 outline-none">
<EmojiPicker.List
data-slot="emoji-picker-list"
className="pb-2 select-none"
components={{
CategoryHeader: ({ category, ...props }) => (
<div
data-slot="emoji-picker-list-category-header"
className="bg-layer-2 px-3 pb-1.5 text-11 font-medium text-tertiary"
{...props}
>
{category.label}
</div>
),
Row: ({ children, ...props }) => (
<div data-slot="emoji-picker-list-row" className="scroll-my-1.5 px-1.5" {...props}>
{children}
</div>
),
Emoji: ({ emoji, ...props }) => (
<button
type="button"
aria-label={emoji?.label ?? emoji?.emoji}
data-slot="emoji-picker-list-emoji"
className="data-active:bg-accent flex size-8 items-center justify-center rounded-md text-16"
{...props}
>
{emoji.emoji}
</button>
),
}}
/>
</EmojiPicker.Viewport>
</EmojiPicker.Root>
);
}
type TPlacement = "top" | "right" | "bottom" | "left" | `${"top" | "right" | "bottom" | "left"}-${"start" | "end"}`;
export interface EmojiReactionPickerProps {
isOpen: boolean;
handleToggle: (value: boolean) => void;
buttonClassName?: string;
closeOnSelect?: boolean;
disabled?: boolean;
dropdownClassName?: string;
label: React.ReactNode;
onChange: (emoji: string) => void;
placement?: TPlacement;
searchPlaceholder?: string;
}
export function EmojiReactionPicker(props: EmojiReactionPickerProps) {
const {
isOpen,
handleToggle,
buttonClassName,
closeOnSelect = true,
disabled = false,
dropdownClassName,
label,
onChange,
placement = "bottom-start",
searchPlaceholder = "Search",
} = props;
const { side, align } = useMemo(() => {
const [rawSide, rawAlign] = placement.split("-") as ["top" | "right" | "bottom" | "left", ("start" | "end")?];
return { side: rawSide, align: rawAlign ?? "start" };
}, [placement]);
const handleEmojiChange = useCallback(
(value: string) => {
onChange(emojiToString(value));
if (closeOnSelect) handleToggle(false);
},
[onChange, closeOnSelect, handleToggle]
);
return (
<Popover open={isOpen} onOpenChange={handleToggle}>
<PopoverTrigger
render={<button type="button" className={cn("outline-none", buttonClassName)} disabled={disabled} />}
>
{label}
</PopoverTrigger>
<PopoverContent side={side} align={align} sideOffset={8}>
{/* bleed past the propel popup's default padding so the picker fills the panel */}
<div className={cn("-m-4 h-80 w-80 overflow-hidden rounded-[inherit]", dropdownClassName)}>
<EmojiRoot onChange={handleEmojiChange} searchPlaceholder={searchPlaceholder} />
</div>
</PopoverContent>
</Popover>
);
}

View File

@@ -0,0 +1,110 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
import { Tooltip } from "@makeplane/propel/components/tooltip";
import { ReactionOutline } from "@makeplane/propel/icons";
// plane imports
import { cn } from "@plane/utils";
export interface EmojiReactionType {
emoji: string;
count: number;
reacted?: boolean;
users?: string[];
}
interface EmojiReactionProps {
emoji: string;
count: number;
reacted?: boolean;
users?: string[];
onReactionClick?: (emoji: string) => void;
className?: string;
}
const MAX_DISPLAY_USERS = 5;
const EmojiReaction = React.forwardRef(function EmojiReaction(
props: EmojiReactionProps,
ref: React.ForwardedRef<HTMLButtonElement>
) {
const { emoji, count, reacted = false, users = [], onReactionClick, className } = props;
const handleClick = () => {
onReactionClick?.(emoji);
};
const button = (
<button
ref={ref}
type="button"
onClick={handleClick}
className={cn(
"inline-flex items-center gap-0.5 rounded-full border px-1.5 text-11 transition-all duration-200",
reacted
? "border-accent-strong bg-accent-primary/10 text-accent-primary"
: "border-subtle bg-surface-1 text-tertiary hover:border-strong hover:bg-surface-2",
className
)}
>
<span className="leading-unset text-14">{emoji}</span>
{count > 0 && <span className="text-11 leading-normal">{count}</span>}
</button>
);
if (users.length === 0) return button;
const displayUsers = users.slice(0, MAX_DISPLAY_USERS);
const remainingCount = users.length - displayUsers.length;
const tooltipLabel = `${emoji} ${displayUsers.join(", ")}${remainingCount > 0 ? ` and ${remainingCount} more` : ""}`;
return <Tooltip label={tooltipLabel}>{button}</Tooltip>;
});
export interface EmojiReactionGroupProps {
reactions: EmojiReactionType[];
onReactionClick?: (emoji: string) => void;
onAddReaction?: () => void;
showAddButton?: boolean;
className?: string;
}
export const EmojiReactionGroup = React.forwardRef(function EmojiReactionGroup(
props: EmojiReactionGroupProps,
ref: React.ForwardedRef<HTMLDivElement>
) {
const { reactions, onReactionClick, onAddReaction, showAddButton = true, className } = props;
return (
<div ref={ref} className={cn("flex flex-wrap items-center gap-2", className)}>
{reactions.map((reaction, index) => (
<EmojiReaction
key={`${reaction.emoji}-${index}`}
emoji={reaction.emoji}
count={reaction.count}
reacted={reaction.reacted}
users={reaction.users}
onReactionClick={onReactionClick}
/>
))}
{showAddButton && (
<Tooltip label="Add reaction">
<button
type="button"
onClick={onAddReaction}
className="grid size-6 place-items-center rounded-full text-tertiary transition-colors outline-none hover:bg-surface-2 hover:text-secondary"
>
<ReactionOutline className="size-3.5" />
</button>
</Tooltip>
)}
</div>
);
});
EmojiReaction.displayName = "EmojiReaction";
EmojiReactionGroup.displayName = "EmojiReactionGroup";

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
/**
* Converts an emoji to a dash-separated string of decimal Unicode code points.
* Handles multi-byte characters and emoji sequences (including skin tone modifiers).
* @param emoji - The emoji string to convert
* @returns Dash-separated string of decimal code points (e.g. "128077-127999")
*/
export function emojiToString(emoji: string): string {
const codePoints: number[] = [];
for (const char of Array.from(emoji)) {
const codePoint = char.codePointAt(0);
if (codePoint !== undefined) codePoints.push(codePoint);
}
return codePoints.join("-");
}
/**
* Converts a dash-separated string of decimal Unicode code points back to the emoji character.
* @param emojiString - Dash-separated string of decimal code points (e.g. "128077-127999")
* @returns The reconstructed emoji string, or "" on failure
*/
export function stringToEmoji(emojiString: string): string {
if (!emojiString) return "";
const decimals = emojiString
.split("-")
.map((s) => Number(s.trim()))
.filter((n) => Number.isFinite(n) && n >= 0 && n <= 0x10ffff);
try {
return decimals.map((decimal) => String.fromCodePoint(decimal)).join("");
} catch {
return "";
}
}

View File

@@ -0,0 +1,10 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
export { stringToEmoji, emojiToString } from "./helpers";
export { EmojiReactionGroup } from "./emoji-reaction";
export type { EmojiReactionType } from "./emoji-reaction";
export { EmojiReactionPicker } from "./emoji-reaction-picker";

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
import type { ISvgIcons } from "../type";
export function PlaneLockup({ width = "253", height = "53", className, color = "currentColor" }: ISvgIcons) {
return (
<svg
width={width}
height={height}
viewBox="0 0 253 53"
fill={color}
xmlns="http://www.w3.org/2000/svg"
className={className}
>
<g clipPath="url(#clip0_27_76)">
<path
d="M217.077 51.4468H210.972L210.98 31.4317C210.717 27.7545 208.786 25.1545 205.03 24.6199C198.587 23.7006 194.663 27.8828 194.21 34.0258L194.218 51.4468H188.041V20.2543H192.158L194.255 26.8846H194.6C195.825 22.8701 198.684 20.4831 202.898 19.5757C210.126 18.0231 216.643 22.2724 217.079 29.8239V51.4468H217.077Z"
fill={color}
/>
<path
d="M163.812 19.7315C158.03 20.9566 153.67 25.2354 153.222 31.264H159.395C159.669 28.3484 161.561 25.9969 164.267 24.9553C167.418 23.7401 172.805 23.8881 175.178 26.6005C176.774 28.4253 177.305 31.5816 175.212 33.2249C172.675 35.2173 166.39 34.6117 163.214 34.7636C158.631 34.9826 154.141 36.8429 153.183 41.7727C151.1 52.5042 165.035 54.9267 171.902 49.8963C174.095 48.2885 175.672 45.8443 176.261 43.1792H176.577L178.788 51.4508H182.61V29.964C182.517 20.3214 171.462 18.108 163.81 19.7315H163.812ZM174.847 41.2637C172.484 45.649 166.958 49.0441 162.218 47.3318C158.44 45.9686 158.268 40.4095 161.973 38.9181C165.613 37.4524 173.732 38.9694 176.368 34.949C176.275 37.0855 175.906 39.2989 174.849 41.2637H174.847Z"
fill={color}
/>
<path
d="M113.082 35.5652V51.4475H106.611V9.00063H124.863C125.007 8.99274 125.141 9.06178 125.283 9.06967C133.732 9.53129 139.297 13.601 139.104 22.6025C138.928 30.7991 132.663 35.2199 124.934 35.5671H113.082V35.5652ZM113.082 14.4828V30.2941H124.088C129.595 29.8956 133.071 27.1022 132.491 21.265C131.967 15.9781 127.802 14.6544 123.107 14.4788C119.795 14.3546 116.402 14.5795 113.082 14.4808V14.4828Z"
fill={color}
/>
<path d="M148.647 9H142.529V51.4468H148.647V9Z" fill={color} />
<path
d="M245.872 42.418C244.911 44.4953 243.138 46.1603 240.934 46.8981C238.368 47.7562 234.479 47.5431 232.083 46.2569C228.67 44.4263 227.145 40.6485 227.137 36.8984H251.891C252.194 33.6908 251.902 30.3648 250.496 27.4333C247.083 20.3177 238.859 17.7236 231.513 19.9429C222.868 22.5568 220.165 31.4162 221.462 39.6543C222.86 48.5611 230.197 52.976 238.958 52.3585C244.884 51.9423 250.271 48.1192 251.788 42.4161H245.874L245.872 42.418ZM227.208 32.4381C227.778 26.2457 233.347 23.2018 239.163 24.4269C243.361 25.3107 245.75 28.9523 245.842 33.1068H227.145C227.145 33.1068 227.19 32.6255 227.208 32.4401V32.4381Z"
fill={color}
/>
<path
d="M44.3223 2.9264C44.3223 0.754665 46.6083 -0.65811 48.5508 0.313121L80.4551 16.2653C82.9294 17.5024 84.4922 20.0321 84.4922 22.7985V48.2487C84.4922 50.4204 82.2071 51.833 80.2646 50.8619L62.3281 41.8932V22.7975C62.3281 20.0311 60.7653 17.5015 58.291 16.2643L44.3223 9.27992V2.9264ZM0 2.92543C8.01645e-05 0.753753 2.28609 -0.659069 4.22852 0.312144L22.1582 9.27699V28.3766C22.1582 31.1428 23.7213 33.6716 26.1953 34.9088L40.1699 41.8952V48.2487C40.1697 50.4202 37.8847 51.832 35.9424 50.861L4.03711 34.9088C1.56305 33.6716 0 31.1428 0 28.3766V2.92543ZM22.1582 2.92543C22.1583 0.753753 24.4443 -0.659069 26.3867 0.312144L44.3223 9.27992V28.3776C44.3223 31.1439 45.8861 33.6727 48.3604 34.9098L62.3281 41.8932V48.2487C62.3279 50.4202 60.0429 51.832 58.1006 50.861L40.1699 41.8952V22.7975C40.1699 20.0311 38.6071 17.5015 36.1328 16.2643L22.1582 9.27699V2.92543Z"
fill={color}
/>
</g>
<defs>
<clipPath id="clip0_27_76">
<rect width="252" height="53" fill="white" />
</clipPath>
</defs>
</svg>
);
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
import type { ISvgIcons } from "../type";
export function PlaneLogo({ width = "85", height = "52", className, color = "currentColor" }: ISvgIcons) {
return (
<svg
width={width}
height={height}
viewBox="0 0 85 52"
fill={color}
xmlns="http://www.w3.org/2000/svg"
className={className}
>
<path
d="M44.3223 2.9264C44.3223 0.754665 46.6083 -0.65811 48.5508 0.313121L80.4551 16.2653C82.9294 17.5024 84.4922 20.0321 84.4922 22.7985V48.2487C84.4922 50.4204 82.2071 51.833 80.2646 50.8619L62.3281 41.8932V22.7975C62.3281 20.0311 60.7653 17.5015 58.291 16.2643L44.3223 9.27992V2.9264ZM0 2.92543C8.01645e-05 0.753753 2.28609 -0.659069 4.22852 0.312144L22.1582 9.27699V28.3766C22.1582 31.1428 23.7213 33.6716 26.1953 34.9088L40.1699 41.8952V48.2487C40.1697 50.4202 37.8847 51.832 35.9424 50.861L4.03711 34.9088C1.56305 33.6716 0 31.1428 0 28.3766V2.92543ZM22.1582 2.92543C22.1583 0.753753 24.4443 -0.659069 26.3867 0.312144L44.3223 9.27992V28.3776C44.3223 31.1439 45.8861 33.6727 48.3604 34.9098L62.3281 41.8932V48.2487C62.3279 50.4202 60.0429 51.832 58.1006 50.861L40.1699 41.8952V22.7975C40.1699 20.0311 38.6071 17.5015 36.1328 16.2643L22.1582 9.27699V2.92543Z"
fill={color}
/>
</svg>
);
}

View File

@@ -0,0 +1,24 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
import type { ISvgIcons } from "../type";
export function CircleDotFullIcon({ className = "text-current", ...rest }: ISvgIcons) {
return (
<svg
viewBox="0 0 24 24"
className={`${className} stroke-1`}
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...rest}
>
<circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
<circle cx="12" cy="12" r="6.25" fill="currentColor" strokeWidth="0.5" />
</svg>
);
}

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
import { CircleDotDashed, Circle } from "lucide-react";
import { CycleIcon } from "../project/cycle-icon";
import { CircleDotFullIcon } from "./circle-dot-full-icon";
import type { ICycleGroupIcon } from "./helper";
import { CYCLE_GROUP_COLORS } from "./helper";
const iconComponents = {
current: CycleIcon,
upcoming: CircleDotDashed,
completed: CircleDotFullIcon,
draft: Circle,
};
export function CycleGroupIcon({
className = "",
color,
cycleGroup,
height = "12px",
width = "12px",
}: ICycleGroupIcon) {
const CycleIconComponent = iconComponents[cycleGroup] || CycleIcon;
return (
<CycleIconComponent
height={height}
width={width}
color={color ?? CYCLE_GROUP_COLORS[cycleGroup]}
className={`flex-shrink-0 ${className}`}
/>
);
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
export interface ICycleGroupIcon {
className?: string;
color?: string;
cycleGroup: TCycleGroups;
height?: string;
width?: string;
}
export type TCycleGroups = "current" | "upcoming" | "completed" | "draft";
export const CYCLE_GROUP_COLORS: {
[key in TCycleGroups]: string;
} = {
current: "#F59E0B",
upcoming: "#3F76FF",
completed: "#16A34A",
draft: "#525252",
};
export const CYCLE_GROUP_I18N_LABELS: {
[key in TCycleGroups]: string;
} = {
current: "current",
upcoming: "common.upcoming",
completed: "common.completed",
draft: "project_cycles.status.draft",
};

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
import type { ISvgIcons } from "./type";
interface IIconWrapper extends ISvgIcons {
children: React.ReactNode;
clipPathId?: string;
viewBox?: string;
}
export function IconWrapper({
width = "16",
height = "16",
className = "text-current",
children,
clipPathId,
viewBox = "0 0 16 16",
...rest
}: IIconWrapper) {
return (
<svg
width={width}
height={height}
viewBox={viewBox}
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
{...rest}
>
{clipPathId ? (
<>
<g clipPath={`url(#${clipPathId})`}>{children}</g>
<defs>
<clipPath id={clipPathId}>
<rect width="16" height="16" fill="white" />
</clipPath>
</defs>
</>
) : (
children
)}
</svg>
);
}

View File

@@ -0,0 +1,15 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
// Domain and brand icons vendored from the workspace @plane/propel icon set —
// @makeplane/propel has no equivalents for these.
export { StateGroupIcon } from "./state/state-group-icon";
export { PriorityIcon } from "./priority-icon";
export type { TIssuePriorities } from "./priority-icon";
export { CycleGroupIcon } from "./cycle/cycle-group-icon";
export { PlaneLogo } from "./brand/plane-logo";
export { PlaneLockup } from "./brand/plane-lockup";
export type { ISvgIcons } from "./type";

View File

@@ -0,0 +1,84 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
import { AlertCircle, Ban, SignalHigh, SignalLow, SignalMedium } from "lucide-react";
import { cn } from "@plane/utils";
export type TIssuePriorities = "urgent" | "high" | "medium" | "low" | "none";
interface IPriorityIcon {
className?: string;
containerClassName?: string;
priority: TIssuePriorities | undefined | null;
size?: number;
withContainer?: boolean;
}
export function PriorityIcon(props: IPriorityIcon) {
const { priority, className = "", containerClassName = "", size = 14, withContainer = false } = props;
const priorityClasses = {
urgent: "bg-layer-2 text-priority-urgent border-priority-urgent",
high: "bg-layer-2 text-priority-high border-priority-high",
medium: "bg-layer-2 text-priority-medium border-priority-medium",
low: "bg-layer-2 text-priority-low border-priority-low",
none: "bg-layer-2 text-priority-none border-priority-none",
};
// get priority icon
const icons = {
urgent: AlertCircle,
high: SignalHigh,
medium: SignalMedium,
low: SignalLow,
none: Ban,
};
const Icon = icons[priority ?? "none"];
if (!Icon) return null;
return (
<>
{withContainer ? (
<div
className={cn(
"flex flex-shrink-0 items-center justify-center rounded-sm border p-0.5",
priorityClasses[priority ?? "none"],
containerClassName
)}
>
<Icon
size={size}
className={cn(
{
"translate-x-[0.0625rem]": priority === "high",
"translate-x-0.5": priority === "medium",
"translate-x-1": priority === "low",
},
className
)}
/>
</div>
) : (
<Icon
size={size}
className={cn(
"flex-shrink-0",
{
"text-priority-urgent": priority === "urgent",
"text-priority-high": priority === "high",
"text-priority-medium": priority === "medium",
"text-priority-low": priority === "low",
"text-priority-none": priority === "none",
},
className
)}
/>
)}
</>
);
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
import { IconWrapper } from "../icon-wrapper";
import type { ISvgIcons } from "../type";
export function CycleIcon({ color = "currentColor", ...rest }: ISvgIcons) {
const clipPathId = React.useId();
return (
<IconWrapper color={color} clipPathId={clipPathId} {...rest}>
<path
d="M14.041 8C14.041 4.66339 11.3365 1.95818 8 1.95801C4.66328 1.95801 1.95801 4.66328 1.95801 8C1.95818 11.3365 4.66339 14.041 8 14.041C11.3364 14.0408 14.0408 11.3364 14.041 8ZM15.291 8C15.2908 12.0268 12.0268 15.2908 8 15.291C3.97303 15.291 0.708184 12.0269 0.708008 8C0.708008 3.97292 3.97292 0.708008 8 0.708008C12.0269 0.708184 15.291 3.97303 15.291 8Z"
fill={color}
/>
<path
d="M7.99951 12.3337C10.3928 12.3337 12.3328 10.3936 12.3328 8.00033C12.3328 5.60709 10.3928 3.66699 7.99951 3.66699V12.3337Z"
fill={color}
/>
</IconWrapper>
);
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
import type { ISvgIcons } from "../type";
import { DashedCircle } from "./dashed-circle";
export function BacklogGroupIcon({ width = "20", height = "20", className, color = "#60646C" }: ISvgIcons) {
// SVG parameters
const viewBoxSize = 16;
const center = viewBoxSize / 2;
const radius = 6;
return (
<svg
height={height}
width={width}
viewBox={`0 0 ${viewBoxSize} ${viewBoxSize}`}
className={className}
xmlns="http://www.w3.org/2000/svg"
>
<DashedCircle center={center} radius={radius} color={color} percentage={0} />
</svg>
);
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
import type { ISvgIcons } from "../type";
export function CancelledGroupIcon({
className = "",
color = "#9AA4BC",
height = "20",
width = "20",
...rest
}: ISvgIcons) {
return (
<svg
height={height}
width={width}
className={className}
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
{...rest}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8 15C11.866 15 15 11.866 15 8C15 4.13401 11.866 1 8 1C4.13401 1 1 4.13401 1 8C1 11.866 4.13401 15 8 15ZM11.1018 4.89826C11.3947 5.19115 11.3947 5.66603 11.1018 5.95892L9.06068 8.00002L11.1018 10.0411C11.3947 10.334 11.3947 10.8089 11.1018 11.1018C10.8089 11.3947 10.334 11.3947 10.0411 11.1018L8.00002 9.06068L5.95892 11.1018C5.66603 11.3947 5.19115 11.3947 4.89826 11.1018C4.60537 10.8089 4.60537 10.334 4.89826 10.0411L6.93936 8.00002L4.89826 5.95892C4.60537 5.66603 4.60537 5.19115 4.89826 4.89826C5.19115 4.60537 5.66603 4.60537 5.95892 4.89826L8.00002 6.93936L10.0411 4.89826C10.334 4.60537 10.8089 4.60537 11.1018 4.89826Z"
fill={color}
/>
</svg>
);
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
import type { ISvgIcons } from "../type";
export function CompletedGroupIcon({
className = "",
color = "#46A758",
height = "20",
width = "20",
...rest
}: ISvgIcons) {
return (
<svg
height={height}
width={width}
className={className}
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
{...rest}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8 15C11.866 15 15 11.866 15 8C15 4.13401 11.866 1 8 1C4.13401 1 1 4.13401 1 8C1 11.866 4.13401 15 8 15ZM11.3587 6.18828C11.6007 5.85214 11.5244 5.38343 11.1882 5.14141C10.8521 4.89938 10.3834 4.97568 10.1414 5.31183L7.03706 9.62335L5.25956 7.97751C4.95563 7.69609 4.4811 7.71434 4.19968 8.01828C3.91826 8.32221 3.93651 8.79673 4.24045 9.07815L6.64045 11.3004C6.79816 11.4464 7.01095 11.5178 7.22481 11.4963C7.43868 11.4749 7.63307 11.3627 7.75865 11.1883L11.3587 6.18828Z"
fill={color}
/>
</svg>
);
}

View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
interface DashedCircleProps {
center: number;
radius: number;
color: string;
percentage: number;
totalSegments?: number;
}
export function DashedCircle({ center, color, percentage, totalSegments = 15 }: DashedCircleProps) {
// Ensure percentage is between 0 and 100
const validPercentage = Math.max(0, Math.min(100, percentage));
// Generate dashed segments for the circle
const generateDashedCircle = () => {
const segments = [];
const angleIncrement = 360 / totalSegments;
for (let i = 0; i < totalSegments; i++) {
// Calculate the angle for this segment (starting from top/12 o'clock position)
const angle = i * angleIncrement - 90; // -90 adjusts to start from top center
// Calculate if this segment should be hidden based on percentage
const segmentStartPercentage = (i / totalSegments) * 100;
const isSegmentVisible = segmentStartPercentage >= validPercentage;
if (isSegmentVisible) {
segments.push(
<g key={i} transform={`translate(${center} ${center}) rotate(${angle})`}>
<line x1={5.75} y1="0" x2={6.5} y2="0" stroke={color} strokeWidth={1.21} strokeLinecap="round" />
</g>
);
}
}
return segments;
};
return <g>{generateDashedCircle()}</g>;
}

View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { EIconSize } from "@plane/constants";
import type { TIntakeStateGroups } from "@plane/types";
export interface IStateGroupIcon {
className?: string;
color?: string;
stateGroup: TStateGroups;
size?: EIconSize;
percentage?: number;
}
export interface IIntakeStateGroupIcon {
className?: string;
color?: string;
stateGroup: TIntakeStateGroups;
size?: EIconSize;
percentage?: number;
}
export type TStateGroups = "backlog" | "unstarted" | "started" | "completed" | "cancelled";
export const STATE_GROUP_COLORS: {
[key in TStateGroups]: string;
} = {
backlog: "#60646C",
unstarted: "#60646C",
started: "#F59E0B",
completed: "#46A758",
cancelled: "#9AA4BC",
};
export const INTAKE_STATE_GROUP_COLORS: { [key in TIntakeStateGroups]: string } = { triage: "#4E5355" };
export const STATE_GROUP_SIZES: {
[key in EIconSize]: string;
} = {
[EIconSize.XS]: "10px",
[EIconSize.SM]: "12px",
[EIconSize.MD]: "14px",
[EIconSize.LG]: "16px",
[EIconSize.XL]: "18px",
};

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
interface ProgressCircleProps {
center: number;
radius: number;
color: string;
strokeWidth: number;
circumference: number;
dashOffset: number;
}
export function ProgressCircle({ center, radius, color, strokeWidth, circumference, dashOffset }: ProgressCircleProps) {
return (
<circle
cx={center}
cy={center}
r={radius}
fill="none"
stroke={color}
strokeWidth={strokeWidth}
strokeDasharray={circumference}
strokeDashoffset={dashOffset}
strokeLinecap="round"
transform={`rotate(-90 ${center} ${center})`}
/>
);
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
import type { ISvgIcons } from "../type";
import { DashedCircle } from "./dashed-circle";
import { ProgressCircle } from "./progress-circle";
// StateIcon component implementation
export function StartedGroupIcon({
width = "20",
height = "20",
className,
color = "#F59E0B",
percentage = 100,
}: ISvgIcons) {
// Ensure percentage is between 0 and 100
const normalized =
typeof percentage === "number"
? percentage <= 1
? percentage * 100 // treat 0-1 as fraction
: percentage // already 0-100
: 100; // fallback
const validPercentage = Math.max(0, Math.min(100, normalized));
// SVG parameters
const viewBoxSize = 16;
const center = viewBoxSize / 2;
const radius = 6;
const strokeWidth = 1.5;
// Calculate the circumference of the circle
const circumference = 2 * Math.PI * radius;
const dashOffset = circumference * (1 - validPercentage / 100);
const dashOffsetSmall = circumference * (1 - 100 / 100);
return (
<svg width={width} height={height} viewBox={`0 0 ${viewBoxSize} ${viewBoxSize}`} className={className}>
{/* Dashed background circle with segments that disappear with progress */}
<DashedCircle center={center} radius={radius} color={color} percentage={validPercentage} />
{/* render smaller circle in the middle */}
<circle
cx={6}
cy={6}
r={3}
strokeWidth={1.5}
strokeLinecap="round"
fill="none"
transform={`rotate(-90 8 6)`}
strokeDasharray={circumference}
strokeDashoffset={dashOffsetSmall}
stroke={color}
/>
{/* Solid progress circle */}
<ProgressCircle
center={center}
radius={radius}
color={color}
strokeWidth={strokeWidth}
circumference={circumference}
dashOffset={dashOffset}
/>
</svg>
);
}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
import { EIconSize } from "@plane/constants";
import { BacklogGroupIcon } from "./backlog-group-icon";
import { CancelledGroupIcon } from "./cancelled-group-icon";
import { CompletedGroupIcon } from "./completed-group-icon";
import type { IStateGroupIcon } from "./helper";
import { STATE_GROUP_COLORS, STATE_GROUP_SIZES } from "./helper";
import { StartedGroupIcon } from "./started-group-icon";
import { UnstartedGroupIcon } from "./unstarted-group-icon";
const iconComponents = {
backlog: BacklogGroupIcon,
cancelled: CancelledGroupIcon,
completed: CompletedGroupIcon,
started: StartedGroupIcon,
unstarted: UnstartedGroupIcon,
};
export function StateGroupIcon({
className = "",
color,
stateGroup,
size = EIconSize.SM,
percentage,
}: IStateGroupIcon) {
const StateIconComponent = iconComponents[stateGroup] || UnstartedGroupIcon;
return (
<StateIconComponent
height={STATE_GROUP_SIZES[size]}
width={STATE_GROUP_SIZES[size]}
color={color ?? STATE_GROUP_COLORS[stateGroup]}
className={`flex-shrink-0 ${className}`}
percentage={percentage}
/>
);
}

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import * as React from "react";
import type { ISvgIcons } from "../type";
import { DashedCircle } from "./dashed-circle";
import { ProgressCircle } from "./progress-circle";
// StateIcon component implementation
export function UnstartedGroupIcon({
width = "20",
height = "20",
className,
color = "#F59E0B",
percentage = 100,
}: ISvgIcons) {
// Ensure percentage is between 0 and 100
const normalized =
typeof percentage === "number"
? percentage <= 1
? percentage * 100 // treat 0-1 as fraction
: percentage // already 0-100
: 100; // fallback
const validPercentage = Math.max(0, Math.min(100, normalized));
// SVG parameters
const viewBoxSize = 16;
const center = viewBoxSize / 2;
const radius = 6;
const strokeWidth = 1.5;
// Calculate the circumference of the circle
const circumference = 2 * Math.PI * radius;
// Calculate the dash offset based on percentage
const dashOffset = circumference * (1 - validPercentage / 100);
return (
<svg width={width} height={height} viewBox={`0 0 ${viewBoxSize} ${viewBoxSize}`} className={className}>
<DashedCircle center={center} radius={radius} color={color} percentage={validPercentage} />
{/* Solid progress circle */}
<ProgressCircle
center={center}
radius={radius}
color={color}
strokeWidth={strokeWidth}
circumference={circumference}
dashOffset={dashOffset}
/>
</svg>
);
}

View File

@@ -0,0 +1,10 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
export interface ISvgIcons extends React.SVGAttributes<SVGElement> {
className?: string | undefined;
percentage?: number;
}

View File

@@ -5,7 +5,7 @@
*/
import { useTheme } from "next-themes";
import { Button } from "@plane/propel/button";
import { Button } from "@makeplane/propel/components/button";
// assets
import InstanceFailureDarkImage from "@/app/assets/instance/instance-failure-dark.svg?url";
import InstanceFailureImage from "@/app/assets/instance/instance-failure.svg?url";
@@ -31,9 +31,7 @@ export function InstanceFailureView() {
</p>
</div>
<div className="flex justify-center">
<Button size="lg" onClick={handleRetry}>
Retry
</Button>
<Button variant="primary" size="lg" stretch="auto" onClick={handleRetry} label="Retry" />
</div>
</div>
</div>

View File

@@ -6,7 +6,7 @@
import { observer } from "mobx-react";
import { useTranslation } from "@plane/i18n";
import { CloseIcon } from "@plane/propel/icons";
import { CloseOutline } from "@makeplane/propel/icons";
// types
import type { TFilters } from "@/types/issue";
// components
@@ -59,7 +59,7 @@ export const AppliedFiltersList = observer(function AppliedFiltersList(props: Pr
className="grid place-items-center text-tertiary hover:text-secondary"
onClick={() => handleRemoveFilter(filterKey, null)}
>
<CloseIcon height={12} width={12} strokeWidth={2} />
<CloseOutline height={12} width={12} strokeWidth={2} />
</button>
</div>
</div>
@@ -71,7 +71,7 @@ export const AppliedFiltersList = observer(function AppliedFiltersList(props: Pr
className="flex items-center gap-2 rounded-md border border-subtle px-2 py-1 text-11 text-tertiary hover:text-secondary"
>
{t("common.clear_all")}
<CloseIcon height={12} width={12} strokeWidth={2} />
<CloseOutline height={12} width={12} strokeWidth={2} />
</button>
</div>
);

View File

@@ -4,7 +4,7 @@
* See the LICENSE file for details.
*/
import { CloseIcon } from "@plane/propel/icons";
import { CloseOutline } from "@makeplane/propel/icons";
// types
import type { IIssueLabel } from "@/types/issue";
@@ -38,7 +38,7 @@ export function AppliedLabelsFilters(props: Props) {
className="grid place-items-center text-tertiary hover:text-secondary"
onClick={() => handleRemove(labelId)}
>
<CloseIcon height={10} width={10} strokeWidth={2} />
<CloseOutline height={10} width={10} strokeWidth={2} />
</button>
</div>
);

View File

@@ -4,8 +4,9 @@
* See the LICENSE file for details.
*/
import { CloseIcon, PriorityIcon } from "@plane/propel/icons";
import type { TIssuePriorities } from "@plane/propel/icons";
import { CloseOutline } from "@makeplane/propel/icons";
import { PriorityIcon } from "@/components/icons";
import type { TIssuePriorities } from "@/components/icons";
type Props = {
handleRemove: (val: string) => void;
@@ -26,7 +27,7 @@ export function AppliedPriorityFilters(props: Props) {
className="grid place-items-center text-tertiary hover:text-secondary"
onClick={() => handleRemove(priority)}
>
<CloseIcon height={10} width={10} strokeWidth={2} />
<CloseOutline height={10} width={10} strokeWidth={2} />
</button>
</div>
))}

View File

@@ -7,7 +7,9 @@
import { observer } from "mobx-react";
// plane imports
import { EIconSize } from "@plane/constants";
import { CloseIcon, StateGroupIcon } from "@plane/propel/icons";
import { CloseOutline } from "@makeplane/propel/icons";
// components
import { StateGroupIcon } from "@/components/icons";
// hooks
import { useStates } from "@/hooks/store/use-state";
@@ -37,7 +39,7 @@ export const AppliedStateFilters = observer(function AppliedStateFilters(props:
className="grid place-items-center text-tertiary hover:text-secondary"
onClick={() => handleRemove(stateId)}
>
<CloseIcon height={10} width={10} strokeWidth={2} />
<CloseOutline height={10} width={10} strokeWidth={2} />
</button>
</div>
);

View File

@@ -9,7 +9,7 @@ import type { Placement } from "@popperjs/core";
import { usePopper } from "react-popper";
import { Popover, Transition } from "@headlessui/react";
// ui
import { Button } from "@plane/propel/button";
import { Button } from "@makeplane/propel/components/button";
type Props = {
children: React.ReactNode;
@@ -35,11 +35,7 @@ export function FiltersDropdown(props: Props) {
return (
<>
<Popover.Button as={React.Fragment}>
<Button ref={setReferenceElement} variant="secondary">
<div className={`${open ? "text-primary" : "text-secondary"}`}>
<span>{title}</span>
</div>
</Button>
<Button ref={setReferenceElement} variant="secondary" size="md" stretch="auto" label={title} />
</Popover.Button>
<Transition
as={Fragment}

View File

@@ -6,7 +6,7 @@
import React from "react";
// icons
import { ChevronDownIcon, ChevronUpIcon } from "@plane/propel/icons";
import { ChevronDownOutline, ChevronUpOutline } from "@makeplane/propel/icons";
interface IFilterHeader {
title: string;
@@ -23,7 +23,7 @@ export function FilterHeader({ title, isPreviewEnabled, handleIsPreviewEnabled }
className="grid h-5 w-5 shrink-0 place-items-center rounded-sm hover:bg-layer-transparent-hover"
onClick={handleIsPreviewEnabled}
>
{isPreviewEnabled ? <ChevronUpIcon height={14} width={14} /> : <ChevronDownIcon height={14} width={14} />}
{isPreviewEnabled ? <ChevronUpOutline height={14} width={14} /> : <ChevronDownOutline height={14} width={14} />}
</button>
</div>
);

View File

@@ -6,7 +6,7 @@
import React from "react";
// plane imports
import { CheckIcon } from "@plane/propel/icons";
import { TickOutline } from "@makeplane/propel/icons";
type Props = {
icon?: React.ReactNode;
@@ -30,7 +30,7 @@ export function FilterOption(props: Props) {
isChecked ? "border-accent-strong bg-accent-primary text-on-color" : "border-strong"
} ${multiple ? "rounded-xs" : "rounded-full"}`}
>
{isChecked && <CheckIcon width={10} height={10} strokeWidth={3} />}
{isChecked && <TickOutline width={10} height={10} strokeWidth={3} />}
</div>
<div className="flex items-center gap-2 truncate">
{icon && <div className="grid w-5 shrink-0 place-items-center">{icon}</div>}

View File

@@ -9,7 +9,8 @@ import { observer } from "mobx-react";
// plane imports
import { ISSUE_PRIORITY_FILTERS } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { PriorityIcon } from "@plane/propel/icons";
// components
import { PriorityIcon } from "@/components/icons";
// local imports
import { FilterHeader } from "./helpers/filter-header";
import { FilterOption } from "./helpers/filter-option";

View File

@@ -6,7 +6,7 @@
import React, { useState } from "react";
import { observer } from "mobx-react";
import { SearchIcon, CloseIcon } from "@plane/propel/icons";
import { SearchOutline, CloseOutline } from "@makeplane/propel/icons";
// types
import type { IIssueFilterOptions, TIssueFilterKeys } from "@/types/issue";
// local imports
@@ -30,7 +30,7 @@ export const FilterSelection = observer(function FilterSelection(props: Props) {
<div className="flex h-full w-full flex-col overflow-hidden">
<div className="p-2.5 pb-0">
<div className="flex items-center gap-1.5 rounded-sm border-[0.5px] border-subtle bg-surface-2 px-1.5 py-1 text-11">
<SearchIcon className="text-placeholder" width={12} height={12} strokeWidth={2} />
<SearchOutline className="text-placeholder" width={12} height={12} strokeWidth={2} />
<input
type="text"
className="w-full bg-surface-2 outline-none placeholder:text-placeholder"
@@ -41,7 +41,7 @@ export const FilterSelection = observer(function FilterSelection(props: Props) {
/>
{filtersSearchQuery !== "" && (
<button type="button" className="grid place-items-center" onClick={() => setFiltersSearchQuery("")}>
<CloseIcon className="text-tertiary" height={12} width={12} strokeWidth={2} />
<CloseOutline className="text-tertiary" height={12} width={12} strokeWidth={2} />
</button>
)}
</div>

View File

@@ -8,8 +8,9 @@ import React, { useState } from "react";
import { observer } from "mobx-react";
// ui
import { EIconSize } from "@plane/constants";
import { StateGroupIcon } from "@plane/propel/icons";
import { Loader } from "@plane/ui";
// components
import { StateGroupIcon } from "@/components/icons";
// hooks
import { useStates } from "@/hooks/store/use-state";
// local imports

View File

@@ -9,7 +9,7 @@ import { observer } from "mobx-react";
import { Link } from "react-router";
import { useParams, useSearchParams } from "next/navigation";
// plane types
import { Tooltip } from "@plane/propel/tooltip";
import { Tooltip } from "@makeplane/propel/components/tooltip";
import type { IIssueDisplayProperties } from "@plane/types";
// plane ui
// plane utils
@@ -57,7 +57,7 @@ const KanbanIssueDetailsBlock = observer(function KanbanIssueDetailsBlock(props:
</WithDisplayPropertiesHOC>
<div className="mb-1.5 line-clamp-1 w-full text-13 text-primary">
<Tooltip tooltipContent={issue.name}>
<Tooltip label={issue.name}>
<span>{issue.name}</span>
</Tooltip>
</div>

View File

@@ -6,7 +6,7 @@
import { observer } from "mobx-react";
import { Circle } from "lucide-react";
import { ChevronDownIcon, ChevronUpIcon } from "@plane/propel/icons";
import { ChevronDownOutline, ChevronUpOutline } from "@makeplane/propel/icons";
// mobx
interface IHeaderSubGroupByCard {
@@ -25,7 +25,11 @@ export const HeaderSubGroupByCard = observer(function HeaderSubGroupByCard(props
onClick={() => toggleExpanded()}
>
<div className="flex h-[20px] w-[20px] flex-shrink-0 items-center justify-center overflow-hidden rounded-xs transition-all hover:bg-layer-1">
{isExpanded ? <ChevronUpIcon width={14} strokeWidth={2} /> : <ChevronDownIcon width={14} strokeWidth={2} />}
{isExpanded ? (
<ChevronUpOutline width={14} strokeWidth={2} />
) : (
<ChevronDownOutline width={14} strokeWidth={2} />
)}
</div>
<div className="flex h-[20px] w-[20px] flex-shrink-0 items-center justify-center overflow-hidden rounded-xs">

View File

@@ -9,7 +9,7 @@ import { observer } from "mobx-react";
import { Link } from "react-router";
import { useParams, useSearchParams } from "next/navigation";
// plane types
import { Tooltip } from "@plane/propel/tooltip";
import { Tooltip } from "@makeplane/propel/components/tooltip";
import type { IIssueDisplayProperties } from "@plane/types";
// plane ui
// plane utils
@@ -79,7 +79,7 @@ export const IssueBlock = observer(function IssueBlock(props: IssueBlockProps) {
onClick={handleIssuePeekOverview}
className="w-full cursor-pointer truncate text-13 text-primary"
>
<Tooltip tooltipContent={issue.name} position="top-start">
<Tooltip label={issue.name} side="top" align="start">
<p className="truncate">{issue.name}</p>
</Tooltip>
</Link>

View File

@@ -6,9 +6,9 @@
import { observer } from "mobx-react";
import { Paperclip } from "lucide-react";
import { LinkIcon, ViewsIcon } from "@plane/propel/icons";
import { LinkOutline, ViewsOutline } from "@makeplane/propel/icons";
// plane imports
import { Tooltip } from "@plane/propel/tooltip";
import { Tooltip } from "@makeplane/propel/components/tooltip";
import type { IIssueDisplayProperties } from "@plane/types";
import { cn } from "@plane/utils";
// components
@@ -138,7 +138,7 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
displayPropertyKey="sub_issue_count"
shouldRenderProperty={(properties) => !!properties.sub_issue_count && !!issue.sub_issues_count}
>
<Tooltip tooltipHeading="Sub-work items" tooltipContent={`${issue.sub_issues_count}`}>
<Tooltip label={`Sub-work items: ${issue.sub_issues_count}`}>
<div
className={cn(
"flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded-sm border-[0.5px] border-strong px-2.5 py-1",
@@ -147,7 +147,7 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
}
)}
>
<ViewsIcon className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
<ViewsOutline className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
<div className="text-11">{issue.sub_issues_count}</div>
</div>
</Tooltip>
@@ -159,7 +159,7 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
displayPropertyKey="attachment_count"
shouldRenderProperty={(properties) => !!properties.attachment_count && !!issue.attachment_count}
>
<Tooltip tooltipHeading="Attachments" tooltipContent={`${issue.attachment_count}`}>
<Tooltip label={`Attachments: ${issue.attachment_count}`}>
<div className="flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded-sm border-[0.5px] border-strong px-2.5 py-1">
<Paperclip className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
<div className="text-11">{issue.attachment_count}</div>
@@ -173,9 +173,9 @@ export const IssueProperties = observer(function IssueProperties(props: IIssuePr
displayPropertyKey="link"
shouldRenderProperty={(properties) => !!properties.link && !!issue.link_count}
>
<Tooltip tooltipHeading="Links" tooltipContent={`${issue.link_count}`}>
<Tooltip label={`Links: ${issue.link_count}`}>
<div className="flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded-sm border-[0.5px] border-strong px-2.5 py-1">
<LinkIcon className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
<LinkOutline className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
<div className="text-11">{issue.link_count}</div>
</div>
</Tooltip>

View File

@@ -6,8 +6,8 @@
import { observer } from "mobx-react";
// plane ui
import { CycleIcon } from "@plane/propel/icons";
import { Tooltip } from "@plane/propel/tooltip";
import { CyclesOutline } from "@makeplane/propel/icons";
import { Tooltip } from "@makeplane/propel/components/tooltip";
// plane utils
import { cn } from "@plane/utils";
//hooks
@@ -24,7 +24,7 @@ export const IssueBlockCycle = observer(function IssueBlockCycle({ cycleId, shou
const cycle = getCycleById(cycleId);
return (
<Tooltip tooltipHeading="Cycle" tooltipContent={cycle?.name ?? "No Cycle"}>
<Tooltip label={`Cycle: ${cycle?.name ?? "No Cycle"}`}>
<div
className={cn(
"flex h-full w-full items-center justify-between gap-1 rounded-sm px-2.5 py-1 text-11 duration-300 focus:outline-none",
@@ -32,7 +32,7 @@ export const IssueBlockCycle = observer(function IssueBlockCycle({ cycleId, shou
)}
>
<div className="flex w-full items-center gap-1.5 text-11">
<CycleIcon className="h-3 w-3 flex-shrink-0" />
<CyclesOutline className="h-3 w-3 flex-shrink-0" />
<div className="max-w-40 truncate">{cycle?.name ?? "No Cycle"}</div>
</div>
</div>

View File

@@ -5,8 +5,8 @@
*/
import { observer } from "mobx-react";
import { DueDatePropertyIcon } from "@plane/propel/icons";
import { Tooltip } from "@plane/propel/tooltip";
import { DueDateOutline } from "@makeplane/propel/icons";
import { Tooltip } from "@makeplane/propel/components/tooltip";
import { cn } from "@plane/utils";
// helpers
import { renderFormattedDate } from "@/helpers/date-time.helper";
@@ -30,14 +30,14 @@ export const IssueBlockDate = observer(function IssueBlockDate(props: Props) {
const formattedDate = renderFormattedDate(due_date);
return (
<Tooltip tooltipHeading="Due Date" tooltipContent={formattedDate}>
<Tooltip label={`Due Date: ${formattedDate ?? "None"}`}>
<div
className={cn("flex h-full items-center gap-1 rounded-sm px-2.5 py-1 text-11 text-primary", {
"text-danger-primary": shouldHighLight && due_date && shouldHighlightIssueDueDate(due_date, state?.group),
"border-[0.5px] border-strong": shouldShowBorder,
})}
>
<DueDatePropertyIcon className="size-3 flex-shrink-0" />
<DueDateOutline className="size-3 flex-shrink-0" />
{formattedDate ? formattedDate : "No Date"}
</div>
</Tooltip>

View File

@@ -5,9 +5,9 @@
*/
import { observer } from "mobx-react";
import { LabelPropertyIcon } from "@plane/propel/icons";
import { LabelsOutline } from "@makeplane/propel/icons";
// plane imports
import { Tooltip } from "@plane/propel/tooltip";
import { Tooltip } from "@makeplane/propel/components/tooltip";
// hooks
import { useLabel } from "@/hooks/store/use-label";
@@ -25,11 +25,11 @@ export const IssueBlockLabels = observer(function IssueBlockLabels({ labelIds, s
if (labels.length <= 0)
return (
<Tooltip position="top" tooltipHeading="Labels" tooltipContent="None">
<Tooltip label="Labels: None">
<div
className={`flex h-full items-center justify-center gap-2 rounded-sm border-[0.5px] border-strong px-2.5 py-1 text-11`}
>
<LabelPropertyIcon className="h-3.5 w-3.5" strokeWidth={2} />
<LabelsOutline className="h-3.5 w-3.5" strokeWidth={2} />
{shouldShowLabel && <span>No Labels</span>}
</div>
</Tooltip>
@@ -40,7 +40,7 @@ export const IssueBlockLabels = observer(function IssueBlockLabels({ labelIds, s
{labels.length <= 2 ? (
<>
{labels.map((label) => (
<Tooltip key={label.id} position="top" tooltipHeading="Labels" tooltipContent={label?.name ?? ""}>
<Tooltip key={label.id} label={`Labels: ${label?.name ?? ""}`}>
<div
key={label?.id}
className={`flex h-full max-w-full flex-shrink-0 items-center overflow-hidden rounded-sm border-[0.5px] border-strong px-2.5 py-1 text-11`}
@@ -62,7 +62,7 @@ export const IssueBlockLabels = observer(function IssueBlockLabels({ labelIds, s
<div
className={`cursor-not-allowed" flex h-full flex-shrink-0 items-center rounded-sm border-[0.5px] border-strong px-2.5 py-1 text-11`}
>
<Tooltip position="top" tooltipHeading="Labels" tooltipContent={labelsString}>
<Tooltip label={`Labels: ${labelsString}`}>
<div className="flex h-full items-center gap-1.5 text-secondary">
<span className="h-2 w-2 flex-shrink-0 rounded-full bg-accent-primary" />
{`${labels.length} Labels`}

View File

@@ -7,7 +7,7 @@
import { observer } from "mobx-react";
// icons
import type { LucideIcon } from "lucide-react";
import { MembersPropertyIcon } from "@plane/propel/icons";
import { MembersOutline } from "@makeplane/propel/icons";
// plane ui
import { Avatar, AvatarGroup } from "@plane/ui";
// plane utils
@@ -56,7 +56,7 @@ export const ButtonAvatars = observer(function ButtonAvatars(props: AvatarProps)
return Icon ? (
<Icon className="h-3 w-3 flex-shrink-0" />
) : (
<MembersPropertyIcon className="mx-[4px] h-3 w-3 flex-shrink-0" />
<MembersOutline className="mx-[4px] h-3 w-3 flex-shrink-0" />
);
});

View File

@@ -6,8 +6,8 @@
import { observer } from "mobx-react";
// plane ui
import { ModuleIcon } from "@plane/propel/icons";
import { Tooltip } from "@plane/propel/tooltip";
import { ModuleOutline } from "@makeplane/propel/icons";
import { Tooltip } from "@makeplane/propel/components/tooltip";
// plane utils
import { cn } from "@plane/utils";
// hooks
@@ -27,7 +27,7 @@ export const IssueBlockModules = observer(function IssueBlockModules({ moduleIds
return (
<div className="relative flex h-full flex-wrap items-center gap-1">
<Tooltip tooltipHeading="Modules" tooltipContent={modulesString}>
<Tooltip label={`Modules: ${modulesString}`}>
{modules.length <= 1 ? (
<div
key={modules?.[0]?.id}
@@ -36,7 +36,7 @@ export const IssueBlockModules = observer(function IssueBlockModules({ moduleIds
})}
>
<div className="flex items-center gap-1.5 text-secondary">
<ModuleIcon className="h-3 w-3 flex-shrink-0" />
<ModuleOutline className="h-3 w-3 flex-shrink-0" />
<div className="text-11">{modules?.[0]?.name ?? "No Modules"}</div>
</div>
</div>

View File

@@ -7,11 +7,12 @@
import { SignalHigh } from "lucide-react";
import { useTranslation } from "@plane/i18n";
// types
import { PriorityIcon } from "@plane/propel/icons";
import { Tooltip } from "@plane/propel/tooltip";
import { Tooltip } from "@makeplane/propel/components/tooltip";
import type { TIssuePriorities } from "@plane/types";
// constants
import { cn, getIssuePriorityFilters } from "@plane/utils";
// components
import { PriorityIcon } from "@/components/icons";
export function IssueBlockPriority({
priority,
@@ -35,7 +36,7 @@ export function IssueBlockPriority({
if (priority_detail === null) return <></>;
return (
<Tooltip tooltipHeading="Priority" tooltipContent={t(priority_detail?.titleTranslationKey || "")}>
<Tooltip label={`Priority: ${t(priority_detail?.titleTranslationKey || "")}`}>
<div
className={cn(
"flex h-full items-center gap-1.5 rounded-sm border-[0.5px] px-2 py-0.5 text-11",

View File

@@ -6,11 +6,12 @@
import { observer } from "mobx-react";
// plane ui
import { StateGroupIcon } from "@plane/propel/icons";
import { Tooltip } from "@plane/propel/tooltip";
import { Tooltip } from "@makeplane/propel/components/tooltip";
import type { TStateGroups } from "@plane/types";
// plane utils
import { cn } from "@plane/utils";
// components
import { StateGroupIcon } from "@/components/icons";
//hooks
import { useStates } from "@/hooks/store/use-state";
@@ -37,7 +38,7 @@ export const IssueBlockState = observer(function IssueBlockState(props: Props) {
if (!state) return null;
return (
<Tooltip tooltipHeading="State" tooltipContent={state.name}>
<Tooltip label={`State: ${state.name}`}>
<div
className={cn("flex h-full w-full items-center justify-between gap-1 rounded-sm px-2.5 py-1 text-11", {
"border-[0.5px] border-strong": shouldShowBorder,

View File

@@ -7,7 +7,8 @@
import { isNil } from "lodash-es";
// types
import { EIconSize, ISSUE_PRIORITIES } from "@plane/constants";
import { CycleGroupIcon, CycleIcon, ModuleIcon, PriorityIcon, StateGroupIcon } from "@plane/propel/icons";
import { CyclesOutline, ModuleOutline } from "@makeplane/propel/icons";
import { CycleGroupIcon, PriorityIcon, StateGroupIcon } from "@/components/icons";
import type {
GroupByColumnTypes,
IGroupByColumn,
@@ -79,7 +80,7 @@ const getCycleColumns = (cycleStore: ICycleStore): IGroupByColumn[] | undefined
cycleGroups.push({
id: "None",
name: "None",
icon: <CycleIcon className="h-3.5 w-3.5" />,
icon: <CyclesOutline className="h-3.5 w-3.5" />,
payload: { cycle_id: null },
});
@@ -98,14 +99,14 @@ const getModuleColumns = (moduleStore: IIssueModuleStore): IGroupByColumn[] | un
moduleGroups.push({
id: moduleInfo.id,
name: moduleInfo.name,
icon: <ModuleIcon className="h-3.5 w-3.5" />,
icon: <ModuleOutline className="h-3.5 w-3.5" />,
payload: { module_ids: [moduleInfo.id] },
});
}) as any;
moduleGroups.push({
id: "None",
name: "None",
icon: <ModuleIcon className="h-3.5 w-3.5" />,
icon: <ModuleOutline className="h-3.5 w-3.5" />,
payload: { module_ids: [] },
});

View File

@@ -5,8 +5,8 @@
*/
import type { TIssueLayout } from "@plane/constants";
import { ListLayoutIcon, BoardLayoutIcon } from "@plane/propel/icons";
import type { ISvgIcons } from "@plane/propel/icons";
import { ListOutline, BoardOutline } from "@makeplane/propel/icons";
import type { ISvgIcons } from "@/components/icons";
export function IssueLayoutIcon({
layout,
@@ -20,9 +20,9 @@ export function IssueLayoutIcon({
switch (layout) {
case "list":
return <ListLayoutIcon {...iconProps} />;
return <ListOutline {...iconProps} />;
case "kanban":
return <BoardLayoutIcon {...iconProps} />;
return <BoardOutline {...iconProps} />;
default:
return null;
}

View File

@@ -10,7 +10,7 @@ import { useRouter, useSearchParams } from "next/navigation";
import { SITES_ISSUE_LAYOUTS } from "@plane/constants";
// plane i18n
import { useTranslation } from "@plane/i18n";
import { Tooltip } from "@plane/propel/tooltip";
import { Tooltip } from "@makeplane/propel/components/tooltip";
// helpers
import { queryParamGenerator } from "@/helpers/query-param-generator";
// hooks
@@ -53,7 +53,7 @@ export const IssuesLayoutSelection = observer(function IssuesLayoutSelection(pro
if (!layoutOptions[layout.key]) return;
return (
<Tooltip key={layout.key} tooltipContent={t(layout.titleTranslationKey)}>
<Tooltip key={layout.key} label={t(layout.titleTranslationKey)}>
<button
type="button"
className={`group grid h-[22px] w-7 place-items-center overflow-hidden rounded-sm bg-layer-transparent transition-all hover:bg-layer-transparent-hover ${

View File

@@ -5,7 +5,7 @@
*/
import { observer } from "mobx-react";
import { ProjectIcon } from "@plane/propel/icons";
import { ProjectsOutline } from "@makeplane/propel/icons";
// components
import { ProjectLogo } from "@/components/common/project-logo";
// store
@@ -32,7 +32,7 @@ export const IssuesNavbarRoot = observer(function IssuesNavbarRoot(props: Props)
</span>
) : (
<span className="grid size-7 shrink-0 place-items-center rounded-sm uppercase">
<ProjectIcon className="size-4" />
<ProjectsOutline className="size-4" />
</span>
)}
<div className="line-clamp-1 max-w-[300px] overflow-hidden text-16 font-medium">

View File

@@ -13,7 +13,7 @@ import { LogOut } from "lucide-react";
import { Popover, Transition } from "@headlessui/react";
// plane imports
import { API_BASE_URL } from "@plane/constants";
import { Button } from "@plane/propel/button";
import { Button } from "@makeplane/propel/components/button";
import { AuthService } from "@plane/services";
import { Avatar } from "@plane/ui";
import { getFileURL } from "@plane/utils";
@@ -119,7 +119,7 @@ export const UserAvatar = observer(function UserAvatar() {
) : (
<div className="flex-shrink-0">
<Link to={`/?next_path=${pathName}?${queryParam}`}>
<Button variant="secondary">Sign in</Button>
<Button variant="secondary" size="md" stretch="auto" label="Sign in" />
</Link>
</div>
)}

View File

@@ -9,7 +9,7 @@ import { observer } from "mobx-react";
import { useForm, Controller } from "react-hook-form";
// plane imports
import type { EditorRefApi } from "@plane/editor";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { TOAST_TYPE, setToast } from "@/providers/toast";
import { SitesFileService } from "@plane/services";
import type { TIssuePublicComment } from "@plane/types";
// editor components

View File

@@ -11,7 +11,7 @@ import { MessageSquare, MoreVertical } from "lucide-react";
import { Menu, Transition } from "@headlessui/react";
// plane imports
import type { EditorRefApi } from "@plane/editor";
import { CheckIcon, CloseIcon } from "@plane/propel/icons";
import { TickOutline, CloseOutline } from "@makeplane/propel/icons";
import type { TIssuePublicComment } from "@plane/types";
import { getFileURL } from "@plane/utils";
// components
@@ -140,14 +140,14 @@ export const CommentCard = observer(function CommentCard(props: Props) {
disabled={isSubmitting}
className="group shadow-md rounded-sm border border-success-strong bg-success-primary p-2 duration-300 hover:bg-success-primary"
>
<CheckIcon className="h-3 w-3 text-on-color" strokeWidth={2} />
<TickOutline className="h-3 w-3 text-on-color" strokeWidth={2} />
</button>
<button
type="button"
className="group shadow-md rounded-sm border border-danger-strong bg-danger-primary p-2 duration-300 hover:bg-danger-primary-hover"
onClick={() => setIsEditing(false)}
>
<CloseIcon className="h-3 w-3 text-on-color" strokeWidth={2} />
<CloseOutline className="h-3 w-3 text-on-color" strokeWidth={2} />
</button>
</div>
</form>

View File

@@ -8,9 +8,8 @@ import React, { useMemo, useState } from "react";
import { observer } from "mobx-react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
// plane imports
import { stringToEmoji } from "@plane/propel/emoji-icon-picker";
import { EmojiReactionGroup, EmojiReactionPicker } from "@plane/propel/emoji-reaction";
import type { EmojiReactionType } from "@plane/propel/emoji-reaction";
import { stringToEmoji, EmojiReactionGroup, EmojiReactionPicker } from "@/components/emoji";
import type { EmojiReactionType } from "@/components/emoji";
// helpers
import { groupReactions } from "@/helpers/emoji.helper";
import { queryParamGenerator } from "@/helpers/query-param-generator";

View File

@@ -9,8 +9,8 @@ import { observer } from "mobx-react";
import { MoveRight } from "lucide-react";
import { Listbox, Transition } from "@headlessui/react";
// ui
import { LinkIcon, CenterPanelIcon, FullScreenPanelIcon, SidePanelIcon } from "@plane/propel/icons";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { LinkOutline, ModalPeekOutline, FullScreenPeekOutline, RightSidePaneOutline } from "@makeplane/propel/icons";
import { TOAST_TYPE, setToast } from "@/providers/toast";
// helpers
import { copyTextToClipboard } from "@/helpers/string.helper";
// hooks
@@ -29,15 +29,15 @@ const PEEK_MODES: {
icon: any;
label: string;
}[] = [
{ key: "side", icon: SidePanelIcon, label: "Side Peek" },
{ key: "side", icon: RightSidePaneOutline, label: "Side Peek" },
{
key: "modal",
icon: CenterPanelIcon,
icon: ModalPeekOutline,
label: "Modal",
},
{
key: "full",
icon: FullScreenPanelIcon,
icon: FullScreenPeekOutline,
label: "Full Screen",
},
];
@@ -60,7 +60,7 @@ export const PeekOverviewHeader = observer(function PeekOverviewHeader(props: Pr
});
};
const Icon = PEEK_MODES.find((m) => m.key === peekMode)?.icon ?? SidePanelIcon;
const Icon = PEEK_MODES.find((m) => m.key === peekMode)?.icon ?? RightSidePaneOutline;
return (
<>
@@ -126,7 +126,7 @@ export const PeekOverviewHeader = observer(function PeekOverviewHeader(props: Pr
className="shrink-0 text-tertiary hover:text-secondary focus:outline-none"
tabIndex={1}
>
<LinkIcon className="h-4 w-4 -rotate-45" />
<LinkOutline className="h-4 w-4 -rotate-45" />
</button>
)}
</div>

View File

@@ -9,7 +9,7 @@ import { Link } from "react-router";
import { usePathname } from "next/navigation";
import { Lock } from "lucide-react";
// plane imports
import { Button } from "@plane/propel/button";
import { Button } from "@makeplane/propel/components/button";
// components
import { AddComment } from "@/components/issues/peek-overview/comment/add-comment";
import { CommentCard } from "@/components/issues/peek-overview/comment/comment-detail-card";
@@ -63,7 +63,7 @@ export const PeekOverviewIssueActivity = observer(function PeekOverviewIssueActi
Sign in to add your comment
</p>
<Link to={`/?next_path=${pathname}`}>
<Button variant="primary">Sign in</Button>
<Button variant="primary" size="md" stretch="auto" label="Sign in" />
</Link>
</div>
))}

View File

@@ -9,14 +9,9 @@ import { useParams } from "next/navigation";
import { LinkIcon } from "lucide-react";
// plane imports
import { useTranslation } from "@plane/i18n";
import {
StatePropertyIcon,
StateGroupIcon,
PriorityPropertyIcon,
DueDatePropertyIcon,
PriorityIcon,
} from "@plane/propel/icons";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import { StateOutline, PriorityOutline, DueDateOutline } from "@makeplane/propel/icons";
import { StateGroupIcon, PriorityIcon } from "@/components/icons";
import { TOAST_TYPE, setToast } from "@/providers/toast";
import { cn, getIssuePriorityFilters } from "@plane/utils";
// helpers
import { renderFormattedDate } from "@/helpers/date-time.helper";
@@ -77,7 +72,7 @@ export const PeekOverviewIssueProperties = observer(function PeekOverviewIssuePr
<div className={`space-y-2 ${mode === "full" ? "pt-3" : ""}`}>
<div className="flex h-8 items-center gap-3">
<div className="flex w-1/4 flex-shrink-0 items-center gap-1 text-13 text-tertiary">
<StatePropertyIcon className="size-4 flex-shrink-0" />
<StateOutline className="size-4 flex-shrink-0" />
<span>State</span>
</div>
<div className="flex w-3/4 items-center gap-1.5 py-0.5 text-13">
@@ -88,7 +83,7 @@ export const PeekOverviewIssueProperties = observer(function PeekOverviewIssuePr
<div className="flex h-8 items-center gap-3">
<div className="flex w-1/4 flex-shrink-0 items-center gap-1 text-13 text-tertiary">
<PriorityPropertyIcon className="size-4 flex-shrink-0" />
<PriorityOutline className="size-4 flex-shrink-0" />
<span>Priority</span>
</div>
<div className="w-3/4">
@@ -113,7 +108,7 @@ export const PeekOverviewIssueProperties = observer(function PeekOverviewIssuePr
<div className="flex h-8 items-center gap-3">
<div className="flex w-1/4 flex-shrink-0 items-center gap-1 text-13 text-tertiary">
<DueDatePropertyIcon className="size-4 flex-shrink-0" />
<DueDateOutline className="size-4 flex-shrink-0" />
<span>Due date</span>
</div>
<div>
@@ -123,7 +118,7 @@ export const PeekOverviewIssueProperties = observer(function PeekOverviewIssuePr
"text-danger-primary": shouldHighlightIssueDueDate(issueDetails.target_date, state?.group),
})}
>
<DueDatePropertyIcon className="size-3" />
<DueDateOutline className="size-3" />
{renderFormattedDate(issueDetails.target_date)}
</div>
) : (

View File

@@ -8,9 +8,8 @@ import { useMemo, useState } from "react";
import { observer } from "mobx-react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
// lib
import { stringToEmoji } from "@plane/propel/emoji-icon-picker";
import { EmojiReactionGroup, EmojiReactionPicker } from "@plane/propel/emoji-reaction";
import type { EmojiReactionType } from "@plane/propel/emoji-reaction";
import { stringToEmoji, EmojiReactionGroup, EmojiReactionPicker } from "@/components/emoji";
import type { EmojiReactionType } from "@/components/emoji";
// helpers
import { groupReactions } from "@/helpers/emoji.helper";
import { queryParamGenerator } from "@/helpers/query-param-generator";

View File

@@ -9,7 +9,7 @@ import { ArrowDown, ArrowUp } from "lucide-react";
import { observer } from "mobx-react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
// plane imports
import { Tooltip } from "@plane/propel/tooltip";
import { Tooltip } from "@makeplane/propel/components/tooltip";
import { cn } from "@plane/utils";
// helpers
import { queryParamGenerator } from "@/helpers/query-param-generator";
@@ -81,20 +81,13 @@ export const IssueVotes = observer(function IssueVotes(props: TIssueVotes) {
<div className="flex items-center gap-2">
{/* upvote button 👇 */}
<Tooltip
tooltipContent={
<div>
{allUpVotes.length > 0 ? (
<>
{allUpVotes
.map((r) => r.actor_details?.display_name)
.splice(0, VOTES_LIMIT)
.join(", ")}
{allUpVotes.length > VOTES_LIMIT && " and " + (allUpVotes.length - VOTES_LIMIT) + " more"}
</>
) : (
"No upvotes yet"
)}
</div>
label={
allUpVotes.length > 0
? `${allUpVotes
.map((r) => r.actor_details?.display_name)
.splice(0, VOTES_LIMIT)
.join(", ")}${allUpVotes.length > VOTES_LIMIT ? ` and ${allUpVotes.length - VOTES_LIMIT} more` : ""}`
: "No upvotes yet"
}
>
<button
@@ -122,20 +115,15 @@ export const IssueVotes = observer(function IssueVotes(props: TIssueVotes) {
{/* downvote button 👇 */}
<Tooltip
tooltipContent={
<div>
{allDownVotes.length > 0 ? (
<>
{allDownVotes
.map((r) => r.actor_details.display_name)
.splice(0, VOTES_LIMIT)
.join(", ")}
{allDownVotes.length > VOTES_LIMIT && " and " + (allDownVotes.length - VOTES_LIMIT) + " more"}
</>
) : (
"No downvotes yet"
)}
</div>
label={
allDownVotes.length > 0
? `${allDownVotes
.map((r) => r.actor_details.display_name)
.splice(0, VOTES_LIMIT)
.join(
", "
)}${allDownVotes.length > VOTES_LIMIT ? ` and ${allDownVotes.length - VOTES_LIMIT} more` : ""}`
: "No downvotes yet"
}
>
<button

View File

@@ -6,7 +6,8 @@
import React from "react";
import { Link } from "react-router";
import { PlaneLockup } from "@plane/propel/icons";
// components
import { PlaneLockup } from "@/components/icons";
export function AuthHeader() {
return (

View File

@@ -10,12 +10,12 @@ import { useTheme } from "next-themes";
import useSWR from "swr";
// plane imports
import { SPACE_BASE_PATH } from "@plane/constants";
import { PlaneLockup } from "@plane/propel/icons";
// assets
import PlaneBackgroundPatternDark from "@/app/assets/auth/background-pattern-dark.svg?url";
import PlaneBackgroundPattern from "@/app/assets/auth/background-pattern.svg?url";
// components
import { LogoSpinner } from "@/components/common/logo-spinner";
import { PlaneLockup } from "@/components/icons";
import { InstanceFailureView } from "@/components/instance/instance-failure-view";
// hooks
import { useInstance } from "@/hooks/store/use-instance";

View File

@@ -4,18 +4,12 @@
* See the LICENSE file for details.
*/
import { useTheme } from "next-themes";
// plane imports
import { Toast } from "@plane/propel/toast";
import { resolveGeneralTheme } from "@plane/utils";
import { ToastWithTheme } from "@/providers/toast";
export function ToastProvider({ children }: { children: React.ReactNode }) {
// themes
const { resolvedTheme } = useTheme();
return (
<>
<Toast theme={resolveGeneralTheme(resolvedTheme)} />
<ToastWithTheme />
{children}
</>
);

View File

@@ -22,10 +22,10 @@
"@fontsource/ibm-plex-mono": "catalog:",
"@fontsource/material-symbols-rounded": "catalog:",
"@headlessui/react": "catalog:",
"@makeplane/propel": "0.2.0",
"@plane/constants": "workspace:*",
"@plane/editor": "workspace:*",
"@plane/i18n": "workspace:*",
"@plane/propel": "workspace:*",
"@plane/services": "workspace:*",
"@plane/types": "workspace:*",
"@plane/ui": "workspace:*",
@@ -36,6 +36,7 @@
"axios": "catalog:",
"clsx": "catalog:",
"date-fns": "catalog:",
"frimousse": "catalog:",
"isbot": "catalog:",
"lodash-es": "catalog:",
"lucide-react": "catalog:",

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { createToastManager, ToastProvider } from "@makeplane/propel/components/toast";
import type { ToastVariant } from "@makeplane/propel/components/toast";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { Icon } from "@makeplane/propel/components/icon";
import { CloseOutline } from "@makeplane/propel/icons";
// Thin wrapper over @makeplane/propel's toast manager to keep the call-site API stable.
export enum TOAST_TYPE {
SUCCESS = "success",
ERROR = "error",
INFO = "info",
WARNING = "warning",
}
const TYPE_TO_VARIANT: Record<TOAST_TYPE, ToastVariant> = {
[TOAST_TYPE.SUCCESS]: "success",
[TOAST_TYPE.ERROR]: "danger",
[TOAST_TYPE.INFO]: "info",
[TOAST_TYPE.WARNING]: "warning",
};
const toastManager = createToastManager();
type SetToastProps = {
type: TOAST_TYPE;
title: string;
message?: string;
};
export const setToast = (props: SetToastProps) =>
toastManager.add({
title: props.title,
description: props.message,
data: { variant: TYPE_TO_VARIANT[props.type] },
});
export function ToastWithTheme() {
return (
<ToastProvider
toastManager={toastManager}
close={
<IconButton variant="ghost" size="sm" aria-label="Close notification" icon={<Icon icon={CloseOutline} />} />
}
/>
);
}

View File

@@ -1,6 +1,9 @@
@import "@plane/tailwind-config/index.css";
@import "@plane/editor/styles";
@import "@plane/propel/styles/react-day-picker.css";
/* Design tokens already arrive via @plane/tailwind-config. The @makeplane/propel styles barrel
re-imports the same token files (identical content) and, crucially, registers propel's dist as a
Tailwind @source so the component class names are generated. */
@import "@makeplane/propel/styles";
@plugin "@tailwindcss/typography";
/* stickies and editor colors

9
pnpm-lock.yaml generated
View File

@@ -873,6 +873,9 @@ importers:
'@headlessui/react':
specifier: 'catalog:'
version: 2.2.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
'@makeplane/propel':
specifier: 0.2.0
version: 0.2.0(@date-fns/tz@1.4.1)(@types/react@19.2.17)(date-fns@4.1.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.1.17)
'@plane/constants':
specifier: workspace:*
version: link:../../packages/constants
@@ -882,9 +885,6 @@ importers:
'@plane/i18n':
specifier: workspace:*
version: link:../../packages/i18n
'@plane/propel':
specifier: workspace:*
version: link:../../packages/propel
'@plane/services':
specifier: workspace:*
version: link:../../packages/services
@@ -915,6 +915,9 @@ importers:
date-fns:
specifier: 'catalog:'
version: 4.1.0
frimousse:
specifier: 'catalog:'
version: 0.3.0(react@19.2.8)(typescript@5.8.3)
isbot:
specifier: 'catalog:'
version: 5.1.31