From f5faa8bd3513e0936891557f894c37278b0dca13 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Wed, 29 Oct 2025 21:18:23 +0530 Subject: [PATCH 1/6] chore: create logo component in propel package --- .../propel/src/emoji-icon-picker/index.ts | 1 + .../propel/src/emoji-icon-picker/logo.tsx | 105 ++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 packages/propel/src/emoji-icon-picker/logo.tsx diff --git a/packages/propel/src/emoji-icon-picker/index.ts b/packages/propel/src/emoji-icon-picker/index.ts index 1ace9dfe823..a662c48d7a2 100644 --- a/packages/propel/src/emoji-icon-picker/index.ts +++ b/packages/propel/src/emoji-icon-picker/index.ts @@ -1,4 +1,5 @@ export * from "./emoji-picker"; export * from "./helper"; +export * from "./logo"; export * from "./lucide-icons"; export * from "./material-icons"; diff --git a/packages/propel/src/emoji-icon-picker/logo.tsx b/packages/propel/src/emoji-icon-picker/logo.tsx new file mode 100644 index 00000000000..264dc6f03af --- /dev/null +++ b/packages/propel/src/emoji-icon-picker/logo.tsx @@ -0,0 +1,105 @@ +"use client"; + +import type { FC } from "react"; +// Due to some weird issue with the import order, the import of useFontFaceObserver +// should be after the imported here rather than some below helper functions as it is in the original file +// eslint-disable-next-line import/order +import useFontFaceObserver from "use-font-face-observer"; +// plane imports +import type { TLogoProps } from "@plane/types"; +// local imports +import { getEmojiSize, stringToEmoji } from "./helper"; +import { LUCIDE_ICONS_LIST } from "./lucide-icons"; + +type Props = { + logo: TLogoProps; + size?: number; + type?: "lucide" | "material"; +}; + +export const Logo: FC = (props) => { + const { logo, size = 16, type = "material" } = props; + + // destructuring the logo object + const { in_use, emoji, icon } = logo; + + // derived values + const value = in_use === "emoji" ? emoji?.value : icon?.name; + const color = icon?.color; + const lucideIcon = LUCIDE_ICONS_LIST.find((item) => item.name === value); + + const isMaterialSymbolsFontLoaded = useFontFaceObserver([ + { + family: `Material Symbols Rounded`, + style: `normal`, + weight: `normal`, + stretch: `condensed`, + }, + ]); + // if no value, return empty fragment + if (!value) return <>; + + if (!isMaterialSymbolsFontLoaded) { + return ( + + ); + } + + // emoji + if (in_use === "emoji") { + return ( + + {stringToEmoji(emoji?.value || "")} + + ); + } + + // icon + if (in_use === "icon") { + return ( + <> + {type === "lucide" ? ( + <> + {lucideIcon && ( + + )} + + ) : ( + + {value} + + )} + + ); + } + + // if no value, return empty fragment + return <>; +}; From f6bd2173a1dfe70e3b05541966b3f4dc241f2577 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Wed, 29 Oct 2025 21:30:57 +0530 Subject: [PATCH 2/6] chore: migrate emoji components from @plane/ui to @plane/propel --- .../web/ce/components/breadcrumbs/project.tsx | 3 +-- .../use-work-item-filters-config.tsx | 3 ++- .../overview/active-project-item.tsx | 4 ++-- .../components/analytics/select/project.tsx | 5 ++--- .../work-items/workitems-insight-table.tsx | 2 +- .../core/components/common/switcher-label.tsx | 2 +- .../list/cycle-list-project-group-header.tsx | 3 +-- .../components/dropdowns/project/base.tsx | 2 +- .../components/home/widgets/recents/page.tsx | 3 +-- .../home/widgets/recents/project.tsx | 2 +- .../filters/applied-filters/project.tsx | 2 +- .../filters/header/filters/project.tsx | 2 +- .../components/issues/issue-layouts/utils.tsx | 3 +-- .../pages/editor/header/logo-picker.tsx | 6 ++---- .../components/pages/editor/header/root.tsx | 4 ++-- apps/web/core/components/pages/list/block.tsx | 2 +- .../components/pages/modals/page-form.tsx | 12 +++++------ .../components/power-k/menus/projects.tsx | 2 +- apps/web/core/components/profile/sidebar.tsx | 4 +--- apps/web/core/components/project/card.tsx | 2 +- .../core/components/project/create/header.tsx | 3 +-- apps/web/core/components/project/form.tsx | 6 ++---- .../components/project/multi-select-modal.tsx | 2 +- .../project/project-feature-update.tsx | 2 +- .../settings/project/sidebar/root.tsx | 2 +- apps/web/core/components/views/form.tsx | 3 +-- .../core/components/views/view-list-item.tsx | 2 +- .../favorite-items/common/helper.tsx | 2 +- .../workspace/sidebar/projects-list-item.tsx | 2 +- apps/web/core/store/pages/base-page.ts | 7 +++---- .../core/extensions/callout/logo-selector.tsx | 20 ++++++++++--------- .../src/core/extensions/callout/utils.ts | 6 +++--- 32 files changed, 57 insertions(+), 68 deletions(-) diff --git a/apps/web/ce/components/breadcrumbs/project.tsx b/apps/web/ce/components/breadcrumbs/project.tsx index 2f6c67bd712..b1137d32990 100644 --- a/apps/web/ce/components/breadcrumbs/project.tsx +++ b/apps/web/ce/components/breadcrumbs/project.tsx @@ -1,12 +1,11 @@ "use client"; import { observer } from "mobx-react"; +import { Logo } from "@plane/propel/emoji-icon-picker"; import { ProjectIcon } from "@plane/propel/icons"; // plane imports import type { ICustomSearchSelectOption } from "@plane/types"; import { BreadcrumbNavigationSearchDropdown, Breadcrumbs } from "@plane/ui"; -// components -import { Logo } from "@/components/common/logo"; import { SwitcherLabel } from "@/components/common/switcher-label"; // hooks import { useProject } from "@/hooks/store/use-project"; diff --git a/apps/web/ce/hooks/work-item-filters/use-work-item-filters-config.tsx b/apps/web/ce/hooks/work-item-filters/use-work-item-filters-config.tsx index 90ad9394dbc..48aee8c7723 100644 --- a/apps/web/ce/hooks/work-item-filters/use-work-item-filters-config.tsx +++ b/apps/web/ce/hooks/work-item-filters/use-work-item-filters-config.tsx @@ -1,6 +1,7 @@ import { useCallback, useMemo } from "react"; import { AtSign, Briefcase, Calendar } from "lucide-react"; // plane imports +import { Logo } from "@plane/propel/emoji-icon-picker"; import { CycleGroupIcon, CycleIcon, @@ -26,7 +27,7 @@ import type { IProject, TWorkItemFilterProperty, } from "@plane/types"; -import { Avatar, Logo } from "@plane/ui"; +import { Avatar } from "@plane/ui"; import { getAssigneeFilterConfig, getCreatedAtFilterConfig, diff --git a/apps/web/core/components/analytics/overview/active-project-item.tsx b/apps/web/core/components/analytics/overview/active-project-item.tsx index b551f494573..4737a8d8118 100644 --- a/apps/web/core/components/analytics/overview/active-project-item.tsx +++ b/apps/web/core/components/analytics/overview/active-project-item.tsx @@ -1,7 +1,7 @@ -import { ProjectIcon } from "@plane/propel/icons"; // plane package imports +import { Logo } from "@plane/propel/emoji-icon-picker"; +import { ProjectIcon } from "@plane/propel/icons"; import { cn } from "@plane/utils"; -import { Logo } from "@/components/common/logo"; // plane web hooks import { useProject } from "@/hooks/store/use-project"; diff --git a/apps/web/core/components/analytics/select/project.tsx b/apps/web/core/components/analytics/select/project.tsx index aee3090268e..b2f77d4ca25 100644 --- a/apps/web/core/components/analytics/select/project.tsx +++ b/apps/web/core/components/analytics/select/project.tsx @@ -1,11 +1,10 @@ "use client"; import { observer } from "mobx-react"; -import { ProjectIcon } from "@plane/propel/icons"; // plane package imports +import { Logo } from "@plane/propel/emoji-icon-picker"; +import { ProjectIcon } from "@plane/propel/icons"; import { CustomSearchSelect } from "@plane/ui"; -// components -import { Logo } from "@/components/common/logo"; // hooks import { useProject } from "@/hooks/store/use-project"; diff --git a/apps/web/core/components/analytics/work-items/workitems-insight-table.tsx b/apps/web/core/components/analytics/work-items/workitems-insight-table.tsx index 0d0d69cb1bb..5de02726835 100644 --- a/apps/web/core/components/analytics/work-items/workitems-insight-table.tsx +++ b/apps/web/core/components/analytics/work-items/workitems-insight-table.tsx @@ -5,13 +5,13 @@ import { useParams } from "next/navigation"; import useSWR from "swr"; import { UserRound } from "lucide-react"; import { useTranslation } from "@plane/i18n"; +import { Logo } from "@plane/propel/emoji-icon-picker"; import { ProjectIcon } from "@plane/propel/icons"; // plane package imports import type { AnalyticsTableDataMap, WorkItemInsightColumns } from "@plane/types"; // plane web components import { Avatar } from "@plane/ui"; import { getFileURL } from "@plane/utils"; -import { Logo } from "@/components/common/logo"; // hooks import { useAnalytics } from "@/hooks/store/use-analytics"; import { useProject } from "@/hooks/store/use-project"; diff --git a/apps/web/core/components/common/switcher-label.tsx b/apps/web/core/components/common/switcher-label.tsx index 7f88c6cdd9d..0e9375da61b 100644 --- a/apps/web/core/components/common/switcher-label.tsx +++ b/apps/web/core/components/common/switcher-label.tsx @@ -1,8 +1,8 @@ import type { FC } from "react"; +import { Logo } from "@plane/propel/emoji-icon-picker"; import type { ISvgIcons } from "@plane/propel/icons"; import type { TLogoProps } from "@plane/types"; import { getFileURL, truncateText } from "@plane/utils"; -import { Logo } from "@/components/common/logo"; type TSwitcherIconProps = { logo_props?: TLogoProps; diff --git a/apps/web/core/components/cycles/list/cycle-list-project-group-header.tsx b/apps/web/core/components/cycles/list/cycle-list-project-group-header.tsx index fdf9b1d42f1..b12ab5786d1 100644 --- a/apps/web/core/components/cycles/list/cycle-list-project-group-header.tsx +++ b/apps/web/core/components/cycles/list/cycle-list-project-group-header.tsx @@ -4,12 +4,11 @@ import type { FC } from "react"; import React from "react"; import { observer } from "mobx-react"; import { ChevronRight } from "lucide-react"; +import { Logo } from "@plane/propel/emoji-icon-picker"; // icons import { Row } from "@plane/ui"; // helpers import { cn } from "@plane/utils"; -// components -import { Logo } from "@/components/common/logo"; import { useProject } from "@/hooks/store/use-project"; type Props = { diff --git a/apps/web/core/components/dropdowns/project/base.tsx b/apps/web/core/components/dropdowns/project/base.tsx index 77a2e85fe2e..8a598eea991 100644 --- a/apps/web/core/components/dropdowns/project/base.tsx +++ b/apps/web/core/components/dropdowns/project/base.tsx @@ -6,11 +6,11 @@ import { Check, ChevronDown, Search } from "lucide-react"; import { Combobox } from "@headlessui/react"; // plane imports import { useTranslation } from "@plane/i18n"; +import { Logo } from "@plane/propel/emoji-icon-picker"; import { ProjectIcon } from "@plane/propel/icons"; import { ComboDropDown } from "@plane/ui"; import { cn } from "@plane/utils"; // components -import { Logo } from "@/components/common/logo"; // hooks import { useDropdown } from "@/hooks/use-dropdown"; // plane web imports diff --git a/apps/web/core/components/home/widgets/recents/page.tsx b/apps/web/core/components/home/widgets/recents/page.tsx index 14a9f0467e1..4c7151e2c59 100644 --- a/apps/web/core/components/home/widgets/recents/page.tsx +++ b/apps/web/core/components/home/widgets/recents/page.tsx @@ -1,11 +1,10 @@ import { useRouter } from "next/navigation"; +import { Logo } from "@plane/propel/emoji-icon-picker"; import { PageIcon } from "@plane/propel/icons"; // plane import import type { TActivityEntityData, TPageEntityData } from "@plane/types"; import { Avatar } from "@plane/ui"; import { calculateTimeAgo, getFileURL, getPageName } from "@plane/utils"; -// components -import { Logo } from "@/components/common/logo"; import { ListItem } from "@/components/core/list"; // hooks import { useMember } from "@/hooks/store/use-member"; diff --git a/apps/web/core/components/home/widgets/recents/project.tsx b/apps/web/core/components/home/widgets/recents/project.tsx index e9e68a49f8f..f8a36de128c 100644 --- a/apps/web/core/components/home/widgets/recents/project.tsx +++ b/apps/web/core/components/home/widgets/recents/project.tsx @@ -1,9 +1,9 @@ import { useRouter } from "next/navigation"; // plane types +import { Logo } from "@plane/propel/emoji-icon-picker"; import type { TActivityEntityData, TProjectEntityData } from "@plane/types"; import { calculateTimeAgo } from "@plane/utils"; // components -import { Logo } from "@/components/common/logo"; import { ListItem } from "@/components/core/list"; import { MemberDropdown } from "@/components/dropdowns/member/dropdown"; // helpers diff --git a/apps/web/core/components/issues/issue-layouts/filters/applied-filters/project.tsx b/apps/web/core/components/issues/issue-layouts/filters/applied-filters/project.tsx index 04efe576ca9..c50aba35660 100644 --- a/apps/web/core/components/issues/issue-layouts/filters/applied-filters/project.tsx +++ b/apps/web/core/components/issues/issue-layouts/filters/applied-filters/project.tsx @@ -1,7 +1,7 @@ import { observer } from "mobx-react"; import { X } from "lucide-react"; // components -import { Logo } from "@/components/common/logo"; +import { Logo } from "@plane/propel/emoji-icon-picker"; // hooks import { useProject } from "@/hooks/store/use-project"; diff --git a/apps/web/core/components/issues/issue-layouts/filters/header/filters/project.tsx b/apps/web/core/components/issues/issue-layouts/filters/header/filters/project.tsx index b04f426204b..f95a7d996e4 100644 --- a/apps/web/core/components/issues/issue-layouts/filters/header/filters/project.tsx +++ b/apps/web/core/components/issues/issue-layouts/filters/header/filters/project.tsx @@ -4,9 +4,9 @@ import React, { useMemo, useState } from "react"; import { sortBy } from "lodash-es"; import { observer } from "mobx-react"; // ui +import { Logo } from "@plane/propel/emoji-icon-picker"; import { Loader } from "@plane/ui"; // components -import { Logo } from "@/components/common/logo"; import { FilterHeader, FilterOption } from "@/components/issues/issue-layouts/filters"; // hooks import { useProject } from "@/hooks/store/use-project"; diff --git a/apps/web/core/components/issues/issue-layouts/utils.tsx b/apps/web/core/components/issues/issue-layouts/utils.tsx index 10ef1875c0f..a7bb471e492 100644 --- a/apps/web/core/components/issues/issue-layouts/utils.tsx +++ b/apps/web/core/components/issues/issue-layouts/utils.tsx @@ -6,6 +6,7 @@ import { clone, isNil, pull, uniq, concat } from "lodash-es"; import scrollIntoView from "smooth-scroll-into-view-if-needed"; // plane types import { EIconSize, ISSUE_PRIORITIES, STATE_GROUPS } from "@plane/constants"; +import { Logo } from "@plane/propel/emoji-icon-picker"; import type { ISvgIcons } from "@plane/propel/icons"; import { CycleGroupIcon, CycleIcon, ModuleIcon, PriorityIcon, StateGroupIcon } from "@plane/propel/icons"; import type { @@ -26,8 +27,6 @@ import { EIssuesStoreType } from "@plane/types"; // plane ui import { Avatar } from "@plane/ui"; import { renderFormattedDate, getFileURL } from "@plane/utils"; -// components -import { Logo } from "@/components/common/logo"; // helpers // store import { store } from "@/lib/store-context"; diff --git a/apps/web/core/components/pages/editor/header/logo-picker.tsx b/apps/web/core/components/pages/editor/header/logo-picker.tsx index 63cb650998c..6ad966e0d4d 100644 --- a/apps/web/core/components/pages/editor/header/logo-picker.tsx +++ b/apps/web/core/components/pages/editor/header/logo-picker.tsx @@ -1,10 +1,8 @@ import { useState } from "react"; import { observer } from "mobx-react"; // plane imports -import { EmojiIconPicker, EmojiIconPickerTypes } from "@plane/ui"; +import { EmojiPicker, EmojiIconPickerTypes, Logo } from "@plane/propel/emoji-icon-picker"; import { cn } from "@plane/utils"; -// components -import { Logo } from "@/components/common/logo"; // store import type { TPageInstance } from "@/store/pages/base-page"; @@ -27,7 +25,7 @@ export const PageEditorHeaderLogoPicker: React.FC = observer((props) => { "max-h-[56px] pointer-events-auto": isLogoSelected, })} > - setIsLogoPickerOpen(val)} className="flex items-center justify-center" diff --git a/apps/web/core/components/pages/editor/header/root.tsx b/apps/web/core/components/pages/editor/header/root.tsx index 85e8546c0fa..36fe1952116 100644 --- a/apps/web/core/components/pages/editor/header/root.tsx +++ b/apps/web/core/components/pages/editor/header/root.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import { observer } from "mobx-react"; import { SmilePlus } from "lucide-react"; // plane imports -import { EmojiIconPicker, EmojiIconPickerTypes } from "@plane/ui"; +import { EmojiPicker, EmojiIconPickerTypes } from "@plane/propel/emoji-icon-picker"; import { cn } from "@plane/utils"; // store import type { TPageInstance } from "@/store/pages/base-page"; @@ -32,7 +32,7 @@ export const PageEditorHeaderRoot: React.FC = observer((props) => { "opacity-100": isTitleEmpty, })} > - setIsLogoPickerOpen(val)} className="flex items-center justify-center" diff --git a/apps/web/core/components/pages/list/block.tsx b/apps/web/core/components/pages/list/block.tsx index 5cf055d2816..c03d349fd48 100644 --- a/apps/web/core/components/pages/list/block.tsx +++ b/apps/web/core/components/pages/list/block.tsx @@ -3,11 +3,11 @@ import type { FC } from "react"; import { useRef } from "react"; import { observer } from "mobx-react"; +import { Logo } from "@plane/propel/emoji-icon-picker"; import { PageIcon } from "@plane/propel/icons"; // plane imports import { getPageName } from "@plane/utils"; // components -import { Logo } from "@/components/common/logo"; import { ListItem } from "@/components/core/list"; import { BlockItemAction } from "@/components/pages/list/block-item-action"; // hooks diff --git a/apps/web/core/components/pages/modals/page-form.tsx b/apps/web/core/components/pages/modals/page-form.tsx index 9005e33a89e..5f1d383f84c 100644 --- a/apps/web/core/components/pages/modals/page-form.tsx +++ b/apps/web/core/components/pages/modals/page-form.tsx @@ -8,13 +8,13 @@ import { Globe2, Lock } from "lucide-react"; import { ETabIndices, EPageAccess } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; import { Button } from "@plane/propel/button"; +import { EmojiPicker, EmojiIconPickerTypes, Logo } from "@plane/propel/emoji-icon-picker"; import { PageIcon } from "@plane/propel/icons"; import type { TPage } from "@plane/types"; -import { EmojiIconPicker, EmojiIconPickerTypes, Input } from "@plane/ui"; -import { convertHexEmojiToDecimal, getTabIndex } from "@plane/utils"; +import { Input } from "@plane/ui"; +import { getTabIndex } from "@plane/utils"; // components import { AccessField } from "@/components/common/access-field"; -import { Logo } from "@/components/common/logo"; // hooks import { usePlatformOS } from "@/hooks/use-platform-os"; @@ -65,7 +65,7 @@ export const PageForm: React.FC = (props) => {

Create page

- setIsOpen(val)} className="flex items-center justify-center flex-shrink0" @@ -86,8 +86,8 @@ export const PageForm: React.FC = (props) => { if (val?.type === "emoji") logoValue = { - value: convertHexEmojiToDecimal(val.value.unified), - url: val.value.imageUrl, + value: val.value, + url: undefined, }; else if (val?.type === "icon") logoValue = val.value; diff --git a/apps/web/core/components/power-k/menus/projects.tsx b/apps/web/core/components/power-k/menus/projects.tsx index c262a52e848..61ff21e9169 100644 --- a/apps/web/core/components/power-k/menus/projects.tsx +++ b/apps/web/core/components/power-k/menus/projects.tsx @@ -2,7 +2,7 @@ import React from "react"; // components -import { Logo } from "@/components/common/logo"; +import { Logo } from "@plane/propel/emoji-icon-picker"; // plane imports import type { TPartialProject } from "@/plane-web/types"; // local imports diff --git a/apps/web/core/components/profile/sidebar.tsx b/apps/web/core/components/profile/sidebar.tsx index 6db83dc0ec1..8f3559e2160 100644 --- a/apps/web/core/components/profile/sidebar.tsx +++ b/apps/web/core/components/profile/sidebar.tsx @@ -13,14 +13,12 @@ import { Disclosure, Transition } from "@headlessui/react"; import { useOutsideClickDetector } from "@plane/hooks"; // types import { useTranslation } from "@plane/i18n"; +import { Logo } from "@plane/propel/emoji-icon-picker"; import { Tooltip } from "@plane/propel/tooltip"; import type { IUserProfileProjectSegregation } from "@plane/types"; // plane ui import { Loader } from "@plane/ui"; import { cn, renderFormattedDate, getFileURL } from "@plane/utils"; -// components -import { Logo } from "@/components/common/logo"; -// helpers // hooks import { useAppTheme } from "@/hooks/store/use-app-theme"; import { useProject } from "@/hooks/store/use-project"; diff --git a/apps/web/core/components/project/card.tsx b/apps/web/core/components/project/card.tsx index d4ba496f0c9..6234b89c4f6 100644 --- a/apps/web/core/components/project/card.tsx +++ b/apps/web/core/components/project/card.tsx @@ -9,6 +9,7 @@ import { ArchiveRestoreIcon, Check, ExternalLink, LinkIcon, Lock, Settings, Tras import { EUserPermissions, EUserPermissionsLevel, IS_FAVORITE_MENU_OPEN } from "@plane/constants"; import { useLocalStorage } from "@plane/hooks"; import { Button } from "@plane/propel/button"; +import { Logo } from "@plane/propel/emoji-icon-picker"; import { setPromiseToast, setToast, TOAST_TYPE } from "@plane/propel/toast"; import { Tooltip } from "@plane/propel/tooltip"; import type { IProject } from "@plane/types"; @@ -16,7 +17,6 @@ import type { TContextMenuItem } from "@plane/ui"; import { Avatar, AvatarGroup, ContextMenu, FavoriteStar } from "@plane/ui"; import { copyUrlToClipboard, cn, getFileURL, renderFormattedDate } from "@plane/utils"; // components -import { Logo } from "@/components/common/logo"; // hooks import { useMember } from "@/hooks/store/use-member"; import { useProject } from "@/hooks/store/use-project"; diff --git a/apps/web/core/components/project/create/header.tsx b/apps/web/core/components/project/create/header.tsx index e22c1565be6..47ec9295d4c 100644 --- a/apps/web/core/components/project/create/header.tsx +++ b/apps/web/core/components/project/create/header.tsx @@ -4,13 +4,12 @@ import { X } from "lucide-react"; // plane imports import { ETabIndices } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; -import { EmojiPicker, EmojiIconPickerTypes } from "@plane/propel/emoji-icon-picker"; +import { EmojiPicker, EmojiIconPickerTypes, Logo } from "@plane/propel/emoji-icon-picker"; // plane types import type { IProject } from "@plane/types"; // plane ui import { getFileURL, getTabIndex } from "@plane/utils"; // components -import { Logo } from "@/components/common/logo"; import { ImagePickerPopover } from "@/components/core/image-picker-popover"; // plane web imports import { ProjectTemplateSelect } from "@/plane-web/components/projects/create/template-select"; diff --git a/apps/web/core/components/project/form.tsx b/apps/web/core/components/project/form.tsx index 1af37a63639..b534a93d8b3 100644 --- a/apps/web/core/components/project/form.tsx +++ b/apps/web/core/components/project/form.tsx @@ -8,14 +8,12 @@ import { NETWORK_CHOICES, PROJECT_TRACKER_ELEMENTS, PROJECT_TRACKER_EVENTS } fro import { useTranslation } from "@plane/i18n"; // plane imports import { Button } from "@plane/propel/button"; -import { EmojiPicker } from "@plane/propel/emoji-icon-picker"; +import { EmojiPicker, EmojiIconPickerTypes, Logo } from "@plane/propel/emoji-icon-picker"; import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import { Tooltip } from "@plane/propel/tooltip"; import type { IProject, IWorkspace } from "@plane/types"; -import { CustomSelect, Input, TextArea, EmojiIconPickerTypes } from "@plane/ui"; +import { CustomSelect, Input, TextArea } from "@plane/ui"; import { renderFormattedDate, getFileURL } from "@plane/utils"; -// components -import { Logo } from "@/components/common/logo"; import { ImagePickerPopover } from "@/components/core/image-picker-popover"; import { TimezoneSelect } from "@/components/global"; // helpers diff --git a/apps/web/core/components/project/multi-select-modal.tsx b/apps/web/core/components/project/multi-select-modal.tsx index 5a0e7b85b17..9f8934f1a78 100644 --- a/apps/web/core/components/project/multi-select-modal.tsx +++ b/apps/web/core/components/project/multi-select-modal.tsx @@ -6,10 +6,10 @@ import { Combobox } from "@headlessui/react"; // plane ui import { useTranslation } from "@plane/i18n"; import { Button } from "@plane/propel/button"; +import { Logo } from "@plane/propel/emoji-icon-picker"; import { Checkbox, EModalPosition, EModalWidth, ModalCore } from "@plane/ui"; // components import { cn } from "@plane/utils"; -import { Logo } from "@/components/common/logo"; import { SimpleEmptyState } from "@/components/empty-state/simple-empty-state-root"; // helpers // hooks diff --git a/apps/web/core/components/project/project-feature-update.tsx b/apps/web/core/components/project/project-feature-update.tsx index e411e33bae1..f2d7e9b8624 100644 --- a/apps/web/core/components/project/project-feature-update.tsx +++ b/apps/web/core/components/project/project-feature-update.tsx @@ -7,9 +7,9 @@ import Link from "next/link"; import { useTranslation } from "@plane/i18n"; // ui import { Button, getButtonStyling } from "@plane/propel/button"; +import { Logo } from "@plane/propel/emoji-icon-picker"; import { Row } from "@plane/ui"; // components -import { Logo } from "@/components/common/logo"; // hooks import { useProject } from "@/hooks/store/use-project"; // plane web imports diff --git a/apps/web/core/components/settings/project/sidebar/root.tsx b/apps/web/core/components/settings/project/sidebar/root.tsx index fe006ae1f5a..51c9ea4a10d 100644 --- a/apps/web/core/components/settings/project/sidebar/root.tsx +++ b/apps/web/core/components/settings/project/sidebar/root.tsx @@ -2,9 +2,9 @@ import { observer } from "mobx-react"; import { useParams } from "next/navigation"; // plane imports import { PROJECT_SETTINGS_CATEGORIES, PROJECT_SETTINGS_CATEGORY } from "@plane/constants"; +import { Logo } from "@plane/propel/emoji-icon-picker"; import { getUserRole } from "@plane/utils"; // components -import { Logo } from "@/components/common/logo"; // hooks import { useProject } from "@/hooks/store/use-project"; // local imports diff --git a/apps/web/core/components/views/form.tsx b/apps/web/core/components/views/form.tsx index 3ad92c769ef..d63611a4579 100644 --- a/apps/web/core/components/views/form.tsx +++ b/apps/web/core/components/views/form.tsx @@ -7,7 +7,7 @@ import { Controller, useForm } from "react-hook-form"; import { ETabIndices, ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; import { Button } from "@plane/propel/button"; -import { EmojiPicker, EmojiIconPickerTypes } from "@plane/propel/emoji-icon-picker"; +import { EmojiPicker, EmojiIconPickerTypes, Logo } from "@plane/propel/emoji-icon-picker"; import { ViewsIcon } from "@plane/propel/icons"; import type { IIssueDisplayFilterOptions, @@ -20,7 +20,6 @@ import { EViewAccess, EIssuesStoreType } from "@plane/types"; import { Input, TextArea } from "@plane/ui"; import { getComputedDisplayFilters, getComputedDisplayProperties, getTabIndex } from "@plane/utils"; // components -import { Logo } from "@/components/common/logo"; import { DisplayFiltersSelection, FiltersDropdown } from "@/components/issues/issue-layouts/filters"; import { WorkItemFiltersRow } from "@/components/work-item-filters/filters-row"; // hooks diff --git a/apps/web/core/components/views/view-list-item.tsx b/apps/web/core/components/views/view-list-item.tsx index 2ec2db74b49..524739a9271 100644 --- a/apps/web/core/components/views/view-list-item.tsx +++ b/apps/web/core/components/views/view-list-item.tsx @@ -4,11 +4,11 @@ import type { FC } from "react"; import { useRef } from "react"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; +import { Logo } from "@plane/propel/emoji-icon-picker"; import { ViewsIcon } from "@plane/propel/icons"; // types import type { IProjectView } from "@plane/types"; // components -import { Logo } from "@/components/common/logo"; import { ListItem } from "@/components/core/list"; // hooks import { usePlatformOS } from "@/hooks/use-platform-os"; diff --git a/apps/web/core/components/workspace/sidebar/favorites/favorite-items/common/helper.tsx b/apps/web/core/components/workspace/sidebar/favorites/favorite-items/common/helper.tsx index 2f1c3a732cf..17c86ea6e21 100644 --- a/apps/web/core/components/workspace/sidebar/favorites/favorite-items/common/helper.tsx +++ b/apps/web/core/components/workspace/sidebar/favorites/favorite-items/common/helper.tsx @@ -1,10 +1,10 @@ "use client"; +import { Logo } from "@plane/propel/emoji-icon-picker"; import { PageIcon } from "@plane/propel/icons"; // plane imports import type { IFavorite, TLogoProps } from "@plane/types"; // components -import { Logo } from "@/components/common/logo"; // plane web constants import { FAVORITE_ITEM_ICONS, FAVORITE_ITEM_LINKS } from "@/plane-web/constants/sidebar-favorites"; diff --git a/apps/web/core/components/workspace/sidebar/projects-list-item.tsx b/apps/web/core/components/workspace/sidebar/projects-list-item.tsx index af60aed6d1f..36d98856069 100644 --- a/apps/web/core/components/workspace/sidebar/projects-list-item.tsx +++ b/apps/web/core/components/workspace/sidebar/projects-list-item.tsx @@ -15,12 +15,12 @@ import { Disclosure, Transition } from "@headlessui/react"; import { EUserPermissions, EUserPermissionsLevel, MEMBER_TRACKER_ELEMENTS } from "@plane/constants"; import { useOutsideClickDetector } from "@plane/hooks"; import { useTranslation } from "@plane/i18n"; +import { Logo } from "@plane/propel/emoji-icon-picker"; import { ArchiveIcon } from "@plane/propel/icons"; import { Tooltip } from "@plane/propel/tooltip"; import { CustomMenu, DropIndicator, DragHandle, ControlLink } from "@plane/ui"; import { cn } from "@plane/utils"; // components -import { Logo } from "@/components/common/logo"; import { LeaveProjectModal } from "@/components/project/leave-project-modal"; import { PublishProjectModal } from "@/components/project/publish-project/modal"; // hooks diff --git a/apps/web/core/store/pages/base-page.ts b/apps/web/core/store/pages/base-page.ts index ca0b0a2266e..b9381f248f8 100644 --- a/apps/web/core/store/pages/base-page.ts +++ b/apps/web/core/store/pages/base-page.ts @@ -2,9 +2,8 @@ import { set } from "lodash-es"; import { action, computed, makeObservable, observable, reaction, runInAction } from "mobx"; // plane imports import { EPageAccess } from "@plane/constants"; +import type { TChangeHandlerProps } from "@plane/propel/emoji-icon-picker"; import type { TDocumentPayload, TLogoProps, TNameDescriptionLoader, TPage } from "@plane/types"; -import type { TChangeHandlerProps } from "@plane/ui"; -import { convertHexEmojiToDecimal } from "@plane/utils"; // plane web store import { ExtendedBasePage } from "@/plane-web/store/pages/extended-base-page"; import type { RootStore } from "@/plane-web/store/root.store"; @@ -448,8 +447,8 @@ export class BasePage extends ExtendedBasePage implements TBasePage { let logoValue = {}; if (value?.type === "emoji") logoValue = { - value: convertHexEmojiToDecimal(value.value.unified), - url: value.value.imageUrl, + value: value.value, + url: undefined, }; else if (value?.type === "icon") logoValue = value.value; diff --git a/packages/editor/src/core/extensions/callout/logo-selector.tsx b/packages/editor/src/core/extensions/callout/logo-selector.tsx index 7a552cd16f0..717345e68f1 100644 --- a/packages/editor/src/core/extensions/callout/logo-selector.tsx +++ b/packages/editor/src/core/extensions/callout/logo-selector.tsx @@ -1,6 +1,7 @@ // plane imports -import { EmojiIconPicker, EmojiIconPickerTypes, Logo, TEmojiLogoProps } from "@plane/ui"; -import { cn, convertHexEmojiToDecimal } from "@plane/utils"; +import { EmojiPicker, EmojiIconPickerTypes, Logo } from "@plane/propel/emoji-icon-picker"; +import type { TLogoProps } from "@plane/types"; +import { cn } from "@plane/utils"; // types import { TCalloutBlockAttributes } from "./types"; // utils @@ -17,7 +18,7 @@ type Props = { export const CalloutBlockLogoSelector: React.FC = (props) => { const { blockAttributes, disabled, handleOpen, isOpen, updateAttributes } = props; - const logoValue: TEmojiLogoProps = { + const logoValue: TLogoProps = { in_use: blockAttributes["data-logo-in-use"], icon: { color: blockAttributes["data-icon-color"], @@ -31,7 +32,7 @@ export const CalloutBlockLogoSelector: React.FC = (props) => { return (
- = (props) => { onChange={(val) => { // construct the new logo value based on the type of value let newLogoValue: Partial = {}; - let newLogoValueToStoreInLocalStorage: TEmojiLogoProps = { + let newLogoValueToStoreInLocalStorage: TLogoProps = { in_use: "emoji", emoji: { value: DEFAULT_CALLOUT_BLOCK_ATTRIBUTES["data-emoji-unicode"], @@ -51,15 +52,16 @@ export const CalloutBlockLogoSelector: React.FC = (props) => { }, }; if (val.type === "emoji") { + // val.value is now a string in decimal format (e.g. "128512") newLogoValue = { - "data-emoji-unicode": convertHexEmojiToDecimal(val.value.unified), - "data-emoji-url": val.value.imageUrl, + "data-emoji-unicode": val.value, + "data-emoji-url": undefined, }; newLogoValueToStoreInLocalStorage = { in_use: "emoji", emoji: { - value: convertHexEmojiToDecimal(val.value.unified), - url: val.value.imageUrl, + value: val.value, + url: undefined, }, }; } else if (val.type === "icon") { diff --git a/packages/editor/src/core/extensions/callout/utils.ts b/packages/editor/src/core/extensions/callout/utils.ts index 8c2cb8f65fc..6d64e0f8a44 100644 --- a/packages/editor/src/core/extensions/callout/utils.ts +++ b/packages/editor/src/core/extensions/callout/utils.ts @@ -1,5 +1,5 @@ // plane imports -import type { TEmojiLogoProps } from "@plane/ui"; +import type { TLogoProps } from "@plane/types"; import { sanitizeHTML } from "@plane/utils"; // types import { @@ -33,7 +33,7 @@ export const getStoredLogo = (): TStoredLogoValue => { if (typeof window !== "undefined") { const storedData = sanitizeHTML(localStorage.getItem("editor-calloutComponent-logo") ?? ""); if (storedData) { - let parsedData: TEmojiLogoProps; + let parsedData: TLogoProps; try { parsedData = JSON.parse(storedData); } catch (error) { @@ -65,7 +65,7 @@ export const getStoredLogo = (): TStoredLogoValue => { return fallBackValues; }; // function to update the stored logo on local storage -export const updateStoredLogo = (value: TEmojiLogoProps): void => { +export const updateStoredLogo = (value: TLogoProps): void => { if (typeof window === "undefined") return; localStorage.setItem("editor-calloutComponent-logo", JSON.stringify(value)); }; From 41393a919f234db1857a6be9e9af44a62e2f0717 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Wed, 29 Oct 2025 21:31:30 +0530 Subject: [PATCH 3/6] chore: remove emoji components and emoji-picker-react dependency --- apps/web/core/components/common/logo.tsx | 103 ------------ packages/ui/package.json | 1 - packages/ui/src/emoji/emoji-icon-helper.tsx | 102 ------------ .../ui/src/emoji/emoji-icon-picker-new.tsx | 140 ---------------- packages/ui/src/emoji/emoji-icon-picker.tsx | 139 ---------------- packages/ui/src/emoji/helpers.ts | 12 -- packages/ui/src/emoji/icons-list.tsx | 153 ------------------ packages/ui/src/emoji/index.ts | 4 - packages/ui/src/emoji/logo.tsx | 104 ------------ packages/ui/src/emoji/lucide-icons-list.tsx | 129 --------------- packages/ui/src/index.ts | 1 - 11 files changed, 888 deletions(-) delete mode 100644 apps/web/core/components/common/logo.tsx delete mode 100644 packages/ui/src/emoji/emoji-icon-helper.tsx delete mode 100644 packages/ui/src/emoji/emoji-icon-picker-new.tsx delete mode 100644 packages/ui/src/emoji/emoji-icon-picker.tsx delete mode 100644 packages/ui/src/emoji/helpers.ts delete mode 100644 packages/ui/src/emoji/icons-list.tsx delete mode 100644 packages/ui/src/emoji/index.ts delete mode 100644 packages/ui/src/emoji/logo.tsx delete mode 100644 packages/ui/src/emoji/lucide-icons-list.tsx diff --git a/apps/web/core/components/common/logo.tsx b/apps/web/core/components/common/logo.tsx deleted file mode 100644 index 12a7ff06bf2..00000000000 --- a/apps/web/core/components/common/logo.tsx +++ /dev/null @@ -1,103 +0,0 @@ -"use client"; - -import type { FC } from "react"; -// Due to some weird issue with the import order, the import of useFontFaceObserver -// should be after the imported here rather than some below helper functions as it is in the original file -// eslint-disable-next-line import/order -import useFontFaceObserver from "use-font-face-observer"; -// plane imports -import { getEmojiSize, LUCIDE_ICONS_LIST, stringToEmoji } from "@plane/propel/emoji-icon-picker"; -import type { TLogoProps } from "@plane/types"; - -type Props = { - logo: TLogoProps; - size?: number; - type?: "lucide" | "material"; -}; - -export const Logo: FC = (props) => { - const { logo, size = 16, type = "material" } = props; - - // destructuring the logo object - const { in_use, emoji, icon } = logo; - - // derived values - const value = in_use === "emoji" ? emoji?.value : icon?.name; - const color = icon?.color; - const lucideIcon = LUCIDE_ICONS_LIST.find((item) => item.name === value); - - const isMaterialSymbolsFontLoaded = useFontFaceObserver([ - { - family: `Material Symbols Rounded`, - style: `normal`, - weight: `normal`, - stretch: `condensed`, - }, - ]); - // if no value, return empty fragment - if (!value) return <>; - - if (!isMaterialSymbolsFontLoaded) { - return ( - - ); - } - - // emoji - if (in_use === "emoji") { - return ( - - {stringToEmoji(emoji?.value || "")} - - ); - } - - // icon - if (in_use === "icon") { - return ( - <> - {type === "lucide" ? ( - <> - {lucideIcon && ( - - )} - - ) : ( - - {value} - - )} - - ); - } - - // if no value, return empty fragment - return <>; -}; diff --git a/packages/ui/package.json b/packages/ui/package.json index 0f0ce349d4f..5dc758241e0 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -47,7 +47,6 @@ "@popperjs/core": "^2.11.8", "@radix-ui/react-scroll-area": "^1.2.3", "clsx": "^2.0.0", - "emoji-picker-react": "^4.5.16", "lodash-es": "catalog:", "lucide-react": "catalog:", "react-color": "^2.19.3", diff --git a/packages/ui/src/emoji/emoji-icon-helper.tsx b/packages/ui/src/emoji/emoji-icon-helper.tsx deleted file mode 100644 index 4643dcb939e..00000000000 --- a/packages/ui/src/emoji/emoji-icon-helper.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { Placement } from "@popperjs/core"; -import { EmojiClickData, Theme } from "emoji-picker-react"; - -export enum EmojiIconPickerTypes { - EMOJI = "emoji", - ICON = "icon", -} - -export const TABS_LIST = [ - { - key: EmojiIconPickerTypes.EMOJI, - title: "Emojis", - }, - { - key: EmojiIconPickerTypes.ICON, - title: "Icons", - }, -]; - -export type TChangeHandlerProps = - | { - type: EmojiIconPickerTypes.EMOJI; - value: EmojiClickData; - } - | { - type: EmojiIconPickerTypes.ICON; - value: { - name: string; - color: string; - }; - }; - -export type TCustomEmojiPicker = { - isOpen: boolean; - handleToggle: (value: boolean) => void; - buttonClassName?: string; - className?: string; - closeOnSelect?: boolean; - defaultIconColor?: string; - defaultOpen?: EmojiIconPickerTypes; - disabled?: boolean; - dropdownClassName?: string; - label: React.ReactNode; - onChange: (value: TChangeHandlerProps) => void; - placement?: Placement; - searchDisabled?: boolean; - searchPlaceholder?: string; - theme?: Theme; - iconType?: "material" | "lucide"; -}; - -export const DEFAULT_COLORS = ["#95999f", "#6d7b8a", "#5e6ad2", "#02b5ed", "#02b55c", "#f2be02", "#e57a00", "#f38e82"]; - -export type TIconsListProps = { - defaultColor: string; - onChange: (val: { name: string; color: string }) => void; - searchDisabled?: boolean; -}; - -/** - * Adjusts the given hex color to ensure it has enough contrast. - * @param {string} hex - The hex color code input by the user. - * @returns {string} - The adjusted hex color code. - */ -export const adjustColorForContrast = (hex: string): string => { - // Ensure hex color is valid - if (!/^#([0-9A-F]{3}){1,2}$/i.test(hex)) { - throw new Error("Invalid hex color code"); - } - - // Convert hex to RGB - let r = 0, - g = 0, - b = 0; - if (hex.length === 4) { - r = parseInt(hex[1] + hex[1], 16); - g = parseInt(hex[2] + hex[2], 16); - b = parseInt(hex[3] + hex[3], 16); - } else if (hex.length === 7) { - r = parseInt(hex[1] + hex[2], 16); - g = parseInt(hex[3] + hex[4], 16); - b = parseInt(hex[5] + hex[6], 16); - } - - // Calculate luminance - const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255; - - // If the color is too light, darken it - if (luminance > 0.5) { - r = Math.max(0, r - 50); - g = Math.max(0, g - 50); - b = Math.max(0, b - 50); - } - - // Convert RGB back to hex - const toHex = (value: number): string => { - const hex = value.toString(16); - return hex.length === 1 ? "0" + hex : hex; - }; - - return `#${toHex(r)}${toHex(g)}${toHex(b)}`; -}; diff --git a/packages/ui/src/emoji/emoji-icon-picker-new.tsx b/packages/ui/src/emoji/emoji-icon-picker-new.tsx deleted file mode 100644 index b8b48102322..00000000000 --- a/packages/ui/src/emoji/emoji-icon-picker-new.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import { Popover, Tab } from "@headlessui/react"; -import EmojiPicker from "emoji-picker-react"; -import React, { useRef, useState } from "react"; -import { usePopper } from "react-popper"; -// plane helpers -import { useOutsideClickDetector } from "@plane/hooks"; -// helpers -import { cn } from "../utils"; -// hooks -import { EmojiIconPickerTypes, TABS_LIST, TCustomEmojiPicker } from "./emoji-icon-helper"; -import { LucideIconsList } from "./lucide-icons-list"; -// helpers - -export const EmojiIconPicker: React.FC = (props) => { - const { - isOpen, - handleToggle, - buttonClassName, - className, - closeOnSelect = true, - defaultIconColor = "#6d7b8a", - defaultOpen = EmojiIconPickerTypes.EMOJI, - disabled = false, - dropdownClassName, - label, - onChange, - placement = "bottom-start", - searchDisabled = false, - searchPlaceholder = "Search", - theme, - } = props; - // refs - const containerRef = useRef(null); - const [referenceElement, setReferenceElement] = useState(null); - const [popperElement, setPopperElement] = useState(null); - // popper-js - const { styles, attributes } = usePopper(referenceElement, popperElement, { - placement, - modifiers: [ - { - name: "preventOverflow", - options: { - padding: 20, - }, - }, - ], - }); - - // close dropdown on outside click - useOutsideClickDetector(containerRef, () => handleToggle(false)); - - return ( - - <> - - - - {isOpen && ( - -
- tab.key === defaultOpen)} - > - - {TABS_LIST.map((tab) => ( - - cn("py-1 text-sm rounded border border-custom-border-200", { - "bg-custom-background-80": selected, - "hover:bg-custom-background-90 focus:bg-custom-background-90": !selected, - }) - } - > - {tab.title} - - ))} - - - - { - onChange({ - type: EmojiIconPickerTypes.EMOJI, - value: val, - }); - if (closeOnSelect) handleToggle(false); - }} - height="20rem" - width="100%" - theme={theme} - searchDisabled={searchDisabled} - searchPlaceholder={searchPlaceholder} - previewConfig={{ - showPreview: false, - }} - lazyLoadEmojis - /> - - - { - onChange({ - type: EmojiIconPickerTypes.ICON, - value: val, - }); - if (closeOnSelect) handleToggle(false); - }} - searchDisabled={searchDisabled} - /> - - - -
-
- )} - -
- ); -}; diff --git a/packages/ui/src/emoji/emoji-icon-picker.tsx b/packages/ui/src/emoji/emoji-icon-picker.tsx deleted file mode 100644 index 04f7fd1a3ec..00000000000 --- a/packages/ui/src/emoji/emoji-icon-picker.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import { Popover, Tab } from "@headlessui/react"; -import EmojiPicker from "emoji-picker-react"; -import React, { useRef, useState } from "react"; -import { usePopper } from "react-popper"; -// plane helpers -import { useOutsideClickDetector } from "@plane/hooks"; -// components -import { cn } from "../utils"; -import { EmojiIconPickerTypes, TABS_LIST, TCustomEmojiPicker } from "./emoji-icon-helper"; -import { IconsList } from "./icons-list"; -// helpers -// hooks - -export const CustomEmojiIconPicker: React.FC = (props) => { - const { - isOpen, - handleToggle, - buttonClassName, - className, - closeOnSelect = true, - defaultIconColor = "#6d7b8a", - defaultOpen = EmojiIconPickerTypes.EMOJI, - disabled = false, - dropdownClassName, - label, - onChange, - placement = "bottom-start", - searchDisabled = false, - searchPlaceholder = "Search", - theme, - } = props; - // refs - const containerRef = useRef(null); - const [referenceElement, setReferenceElement] = useState(null); - const [popperElement, setPopperElement] = useState(null); - // popper-js - const { styles, attributes } = usePopper(referenceElement, popperElement, { - placement, - modifiers: [ - { - name: "preventOverflow", - options: { - padding: 20, - }, - }, - ], - }); - - // close dropdown on outside click - useOutsideClickDetector(containerRef, () => handleToggle(false)); - - return ( - - <> - - - - {isOpen && ( - -
- tab.key === defaultOpen)} - > - - {TABS_LIST.map((tab) => ( - - cn("py-1 text-sm rounded border border-custom-border-200", { - "bg-custom-background-80": selected, - "hover:bg-custom-background-90 focus:bg-custom-background-90": !selected, - }) - } - > - {tab.title} - - ))} - - - - { - onChange({ - type: EmojiIconPickerTypes.EMOJI, - value: val, - }); - if (closeOnSelect) handleToggle(false); - }} - height="20rem" - width="100%" - theme={theme} - searchDisabled={searchDisabled} - searchPlaceholder={searchPlaceholder} - previewConfig={{ - showPreview: false, - }} - /> - - - { - onChange({ - type: EmojiIconPickerTypes.ICON, - value: val, - }); - if (closeOnSelect) handleToggle(false); - }} - searchDisabled={searchDisabled} - /> - - - -
-
- )} - -
- ); -}; diff --git a/packages/ui/src/emoji/helpers.ts b/packages/ui/src/emoji/helpers.ts deleted file mode 100644 index aa04f86036f..00000000000 --- a/packages/ui/src/emoji/helpers.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const emojiCodeToUnicode = (emoji: string) => { - if (!emoji) return ""; - - // convert emoji code to unicode - const uniCodeEmoji = emoji - .toString() - .split("-") - .map((emoji) => parseInt(emoji, 10).toString(16)) - .join("-"); - - return uniCodeEmoji; -}; diff --git a/packages/ui/src/emoji/icons-list.tsx b/packages/ui/src/emoji/icons-list.tsx deleted file mode 100644 index 6a21f02cf06..00000000000 --- a/packages/ui/src/emoji/icons-list.tsx +++ /dev/null @@ -1,153 +0,0 @@ -"use client"; - -import { Search } from "lucide-react"; -import React, { useEffect, useState } from "react"; -// icons -import useFontFaceObserver from "use-font-face-observer"; -import { InfoIcon } from "@plane/propel/icons"; -import { MATERIAL_ICONS_LIST } from ".."; -import { Input } from "../form-fields"; -import { cn } from "../utils"; -// components -// hooks -// helpers -import { DEFAULT_COLORS, TIconsListProps, adjustColorForContrast } from "./emoji-icon-helper"; - -export const IconsList: React.FC = (props) => { - const { defaultColor, onChange, searchDisabled = false } = props; - // states - const [activeColor, setActiveColor] = useState(defaultColor); - const [showHexInput, setShowHexInput] = useState(false); - const [hexValue, setHexValue] = useState(""); - const [isInputFocused, setIsInputFocused] = useState(false); - const [query, setQuery] = useState(""); - - useEffect(() => { - if (DEFAULT_COLORS.includes(defaultColor.toLowerCase())) setShowHexInput(false); - else { - setHexValue(defaultColor.slice(1, 7)); - setShowHexInput(true); - } - }, [defaultColor]); - - const filteredArray = MATERIAL_ICONS_LIST.filter((icon) => icon.name.toLowerCase().includes(query.toLowerCase())); - - const isMaterialSymbolsFontLoaded = useFontFaceObserver([ - { - family: `Material Symbols Rounded`, - style: `normal`, - weight: `normal`, - stretch: `condensed`, - }, - ]); - - return ( - <> -
- {!searchDisabled && ( -
-
setIsInputFocused(true)} - onBlur={() => setIsInputFocused(false)} - > - - setQuery(e.target.value)} - className="text-[1rem] border-none p-0 h-full w-full " - /> -
-
- )} -
- {showHexInput ? ( -
- - HEX - # - { - const value = e.target.value; - setHexValue(value); - if (/^[0-9A-Fa-f]{6}$/.test(value)) setActiveColor(adjustColorForContrast(`#${value}`)); - }} - className="flex-grow pl-0 text-xs text-custom-text-200" - mode="true-transparent" - autoFocus - /> -
- ) : ( - DEFAULT_COLORS.map((curCol) => ( - - )) - )} - -
-
- -

Colors will be adjusted to ensure sufficient contrast.

-
-
-
- {filteredArray.map((icon) => ( - - ))} -
- - ); -}; diff --git a/packages/ui/src/emoji/index.ts b/packages/ui/src/emoji/index.ts deleted file mode 100644 index c87b6cd238c..00000000000 --- a/packages/ui/src/emoji/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from "./emoji-icon-picker-new"; -export * from "./emoji-icon-picker"; -export * from "./emoji-icon-helper"; -export * from "./logo"; diff --git a/packages/ui/src/emoji/logo.tsx b/packages/ui/src/emoji/logo.tsx deleted file mode 100644 index 1d09991c108..00000000000 --- a/packages/ui/src/emoji/logo.tsx +++ /dev/null @@ -1,104 +0,0 @@ -"use client"; - -import { Emoji } from "emoji-picker-react"; -import React, { FC } from "react"; -import useFontFaceObserver from "use-font-face-observer"; -// local imports -import { LUCIDE_ICONS_LIST } from ".."; -import { emojiCodeToUnicode } from "./helpers"; - -export type TEmojiLogoProps = { - in_use: "emoji" | "icon"; - emoji?: { - value?: string; - url?: string; - }; - icon?: { - name?: string; - color?: string; - }; -}; - -type Props = { - logo: TEmojiLogoProps; - size?: number; - type?: "lucide" | "material"; -}; - -export const Logo: FC = (props) => { - const { logo, size = 16, type = "material" } = props; - - // destructuring the logo object - const { in_use, emoji, icon } = logo; - - // if no in_use value, return empty fragment - if (!in_use) return <>; - - // derived values - const value = in_use === "emoji" ? emoji?.value : icon?.name; - const color = icon?.color; - const lucideIcon = LUCIDE_ICONS_LIST.find((item) => item.name === value); - - const isMaterialSymbolsFontLoaded = useFontFaceObserver([ - { - family: `Material Symbols Rounded`, - style: `normal`, - weight: `normal`, - stretch: `condensed`, - }, - ]); - // if no value, return empty fragment - if (!value) return <>; - - if (!isMaterialSymbolsFontLoaded) { - return ( - - ); - } - - // emoji - if (in_use === "emoji") { - return ; - } - - // icon - if (in_use === "icon") { - return ( - <> - {type === "lucide" ? ( - <> - {lucideIcon && ( - - )} - - ) : ( - - {value} - - )} - - ); - } - - // if no value, return empty fragment - return <>; -}; diff --git a/packages/ui/src/emoji/lucide-icons-list.tsx b/packages/ui/src/emoji/lucide-icons-list.tsx deleted file mode 100644 index e9f0517ee76..00000000000 --- a/packages/ui/src/emoji/lucide-icons-list.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import { Search } from "lucide-react"; -import React, { useEffect, useState } from "react"; -// local imports -import { InfoIcon } from "@plane/propel/icons"; -import { LUCIDE_ICONS_LIST } from ".."; -import { Input } from "../form-fields"; -import { cn } from "../utils"; -import { DEFAULT_COLORS, TIconsListProps, adjustColorForContrast } from "./emoji-icon-helper"; - -export const LucideIconsList: React.FC = (props) => { - const { defaultColor, onChange, searchDisabled = false } = props; - // states - const [activeColor, setActiveColor] = useState(defaultColor); - const [showHexInput, setShowHexInput] = useState(false); - const [hexValue, setHexValue] = useState(""); - const [isInputFocused, setIsInputFocused] = useState(false); - const [query, setQuery] = useState(""); - - useEffect(() => { - if (DEFAULT_COLORS.includes(defaultColor.toLowerCase())) setShowHexInput(false); - else { - setHexValue(defaultColor.slice(1, 7)); - setShowHexInput(true); - } - }, [defaultColor]); - - const filteredArray = LUCIDE_ICONS_LIST.filter((icon) => icon.name.toLowerCase().includes(query.toLowerCase())); - - return ( - <> -
- {!searchDisabled && ( -
-
setIsInputFocused(true)} - onBlur={() => setIsInputFocused(false)} - > - - setQuery(e.target.value)} - className="text-[1rem] border-none p-0 h-full w-full " - /> -
-
- )} -
- {showHexInput ? ( -
- - HEX - # - { - const value = e.target.value; - setHexValue(value); - if (/^[0-9A-Fa-f]{6}$/.test(value)) setActiveColor(adjustColorForContrast(`#${value}`)); - }} - className="flex-grow pl-0 text-xs text-custom-text-200" - mode="true-transparent" - autoFocus - /> -
- ) : ( - DEFAULT_COLORS.map((curCol) => ( - - )) - )} - -
-
- -

Colors will be adjusted to ensure sufficient contrast.

-
-
-
- {filteredArray.map((icon) => ( - - ))} -
- - ); -}; diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 8e86dc635cd..36aeaa97678 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -12,7 +12,6 @@ export * from "./drag-handle"; export * from "./drop-indicator"; export * from "./dropdown"; export * from "./dropdowns"; -export * from "./emoji"; export * from "./favorite-star"; export * from "./form-fields"; export * from "./header"; From b8c9594609c7631350baab16d740725d756cdaa1 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Wed, 29 Oct 2025 21:31:53 +0530 Subject: [PATCH 4/6] chore: update pnpm lockfile after emoji migration --- packages/editor/package.json | 1 + pnpm-lock.yaml | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/editor/package.json b/packages/editor/package.json index 985a3f4405c..bf0457fd897 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -43,6 +43,7 @@ "@plane/hooks": "workspace:*", "@plane/types": "workspace:*", "@plane/ui": "workspace:*", + "@plane/propel": "workspace:*", "@plane/utils": "workspace:*", "@tiptap/core": "catalog:", "@tiptap/extension-blockquote": "^2.22.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bca8100f89d..03498ebc752 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -715,6 +715,9 @@ importers: '@plane/hooks': specifier: workspace:* version: link:../hooks + '@plane/propel': + specifier: workspace:* + version: link:../propel '@plane/types': specifier: workspace:* version: link:../types @@ -1241,9 +1244,6 @@ importers: clsx: specifier: ^2.0.0 version: 2.1.1 - emoji-picker-react: - specifier: ^4.5.16 - version: 4.12.2(react@18.3.1) lodash-es: specifier: 'catalog:' version: 4.17.21 @@ -2170,8 +2170,8 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} - '@napi-rs/wasm-runtime@1.0.5': - resolution: {integrity: sha512-TBr9Cf9onSAS2LQ2+QHx6XcC6h9+RIzJgbqG3++9TUZSH204AwEy5jg3BTQ0VATsyoGj4ee49tN/y6rvaOOtcg==} + '@napi-rs/wasm-runtime@1.0.3': + resolution: {integrity: sha512-rZxtMsLwjdXkMUGC3WwsPwLNVqVqnTJT6MNIB6e+5fhMcSCPP0AOsNWuMQ5mdCq6HNjs/ZeWAEchpqeprqBD2Q==} '@next/env@14.2.32': resolution: {integrity: sha512-n9mQdigI6iZ/DF6pCTwMKeWgF2e8lg7qgt5M7HXMLtyhZYMnf/u905M18sSpPmHL9MKp9JHo56C6jrD2EvWxng==} @@ -9047,7 +9047,7 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@napi-rs/wasm-runtime@1.0.5': + '@napi-rs/wasm-runtime@1.0.3': dependencies: '@emnapi/core': 1.5.0 '@emnapi/runtime': 1.5.0 @@ -9734,7 +9734,7 @@ snapshots: '@rolldown/binding-wasm32-wasi@1.0.0-beta.34': dependencies: - '@napi-rs/wasm-runtime': 1.0.5 + '@napi-rs/wasm-runtime': 1.0.3 optional: true '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.34': From 82c2e4b069e63b5051603b5439f373a97d429409 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Fri, 31 Oct 2025 14:35:46 +0530 Subject: [PATCH 5/6] chore: code refactor --- packages/editor/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/editor/package.json b/packages/editor/package.json index 5b7108a3be4..37280fa047c 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -45,7 +45,6 @@ "@plane/ui": "workspace:*", "@plane/propel": "workspace:*", "@plane/utils": "workspace:*", - "@plane/propel": "workspace:*", "@tiptap/core": "catalog:", "@tiptap/extension-blockquote": "^2.22.3", "@tiptap/extension-character-count": "^2.22.3", @@ -99,4 +98,4 @@ "nextjs", "react" ] -} +} \ No newline at end of file From bedf9a6446556fdd2a3ad39278078d54da4e5cd0 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia Date: Fri, 31 Oct 2025 14:42:51 +0530 Subject: [PATCH 6/6] chore: code refactor --- packages/editor/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor/package.json b/packages/editor/package.json index 37280fa047c..bf0457fd897 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -98,4 +98,4 @@ "nextjs", "react" ] -} \ No newline at end of file +}