Skip to content

Commit

Permalink
[WEB-393] feat: new emoji picker using emoji-picker-react (#3868)
Browse files Browse the repository at this point in the history
* chore: emoji-picker-react package added

* chore: emoji and emoji picker component added

* chore: emoji picker custom style added

* chore: migration of the emoji's

* chore: migration changes

* chore: project logo prop

* chore: added logo props in the serializer

* chore: removed unused keys

* chore: implement emoji picker throughout the web app

* style: emoji icon picker

* chore: update project logo renderer in the space app

* chore: migrations fixes

---------

Co-authored-by: Anmol Singh Bhatia <[email protected]>
Co-authored-by: NarayanBavisetti <[email protected]>
  • Loading branch information
3 people authored Mar 6, 2024
1 parent b3d3c0f commit e4f48d6
Show file tree
Hide file tree
Showing 58 changed files with 1,513 additions and 2,462 deletions.
3 changes: 1 addition & 2 deletions apiserver/plane/app/serializers/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ class Meta:
"identifier",
"name",
"cover_image",
"icon_prop",
"emoji",
"logo_props",
"description",
]
read_only_fields = fields
Expand Down
4 changes: 0 additions & 4 deletions apiserver/plane/app/views/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -1366,10 +1366,6 @@ def get(self, request, slug, user_id):
)
.values(
"id",
"name",
"identifier",
"emoji",
"icon_prop",
"created_issues",
"assigned_issues",
"completed_issues",
Expand Down
18 changes: 0 additions & 18 deletions apiserver/plane/db/migrations/0061_alter_issuelink_url.py

This file was deleted.

54 changes: 54 additions & 0 deletions apiserver/plane/db/migrations/0061_project_logo_props.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Generated by Django 4.2.7 on 2024-03-03 16:25

from django.db import migrations, models


class Migration(migrations.Migration):

def update_project_logo_props(apps, schema_editor):
Project = apps.get_model("db", "Project")

bulk_update_project_logo = []
# Iterate through projects and update logo_props
for project in Project.objects.all():
project.logo_props["in_use"] = "emoji" if project.emoji else "icon"
project.logo_props["emoji"] = {
"value": project.emoji if project.emoji else "",
"url": "",
}
project.logo_props["icon"] = {
"name": (
project.icon_prop.get("name", "")
if project.icon_prop
else ""
),
"color": (
project.icon_prop.get("color", "")
if project.icon_prop
else ""
),
}
bulk_update_project_logo.append(project)

# Bulk update logo_props for all projects
Project.objects.bulk_update(
bulk_update_project_logo, ["logo_props"], batch_size=1000
)

dependencies = [
("db", "0060_cycle_progress_snapshot"),
]

operations = [
migrations.AlterField(
model_name="issuelink",
name="url",
field=models.TextField(),
),
migrations.AddField(
model_name="project",
name="logo_props",
field=models.JSONField(default=dict),
),
migrations.RunPython(update_project_logo_props),
]
1 change: 1 addition & 0 deletions apiserver/plane/db/models/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class Project(BaseModel):
close_in = models.IntegerField(
default=0, validators=[MinValueValidator(0), MaxValueValidator(12)]
)
logo_props = models.JSONField(default=dict)
default_state = models.ForeignKey(
"db.State",
on_delete=models.SET_NULL,
Expand Down
27 changes: 15 additions & 12 deletions packages/types/src/projects.d.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
import { EUserProjectRoles } from "constants/project";
import type {
IProjectViewProps,
IUser,
IUserLite,
IUserMemberLite,
IWorkspace,
IWorkspaceLite,
TStateGroups,
} from ".";

export type TProjectLogoProps = {
in_use: "emoji" | "icon";
emoji?: {
value?: string;
url?: string;
};
icon?: {
name?: string;
color?: string;
};
};

export interface IProject {
archive_in: number;
close_in: number;
Expand All @@ -21,24 +35,13 @@ export interface IProject {
default_assignee: IUser | string | null;
default_state: string | null;
description: string;
emoji: string | null;
emoji_and_icon:
| string
| {
name: string;
color: string;
}
| null;
estimate: string | null;
icon_prop: {
name: string;
color: string;
} | null;
id: string;
identifier: string;
is_deployed: boolean;
is_favorite: boolean;
is_member: boolean;
logo_props: TProjectLogoProps;
member_role: EUserProjectRoles | null;
members: IProjectMemberLite[];
name: string;
Expand Down
4 changes: 0 additions & 4 deletions packages/types/src/users.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,7 @@ export interface IUserProfileProjectSegregation {
assigned_issues: number;
completed_issues: number;
created_issues: number;
emoji: string | null;
icon_prop: null;
id: string;
identifier: string;
name: string;
pending_issues: number;
}[];
user_data: {
Expand Down
1 change: 1 addition & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@headlessui/react": "^1.7.17",
"@popperjs/core": "^2.11.8",
"clsx": "^2.0.0",
"emoji-picker-react": "^4.5.16",
"react-color": "^2.19.3",
"react-dom": "^18.2.0",
"react-popper": "^2.3.0",
Expand Down
169 changes: 169 additions & 0 deletions packages/ui/src/emoji/emoji-icon-picker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import React, { useState } from "react";
import { usePopper } from "react-popper";
import EmojiPicker, { EmojiClickData, Theme } from "emoji-picker-react";
import { Popover, Tab } from "@headlessui/react";
import { Placement } from "@popperjs/core";
// components
import { IconsList } from "./icons-list";
// helpers
import { cn } from "../../helpers";

export enum EmojiIconPickerTypes {
EMOJI = "emoji",
ICON = "icon",
}

type TChangeHandlerProps =
| {
type: EmojiIconPickerTypes.EMOJI;
value: EmojiClickData;
}
| {
type: EmojiIconPickerTypes.ICON;
value: {
name: string;
color: string;
};
};

export type TCustomEmojiPicker = {
buttonClassName?: string;
className?: string;
closeOnSelect?: boolean;
defaultIconColor?: string;
defaultOpen?: EmojiIconPickerTypes;
disabled?: boolean;
dropdownClassName?: string;
label: React.ReactNode;
onChange: (value: TChangeHandlerProps) => void;
placement?: Placement;
searchPlaceholder?: string;
theme?: Theme;
};

const TABS_LIST = [
{
key: EmojiIconPickerTypes.EMOJI,
title: "Emojis",
},
{
key: EmojiIconPickerTypes.ICON,
title: "Icons",
},
];

export const CustomEmojiIconPicker: React.FC<TCustomEmojiPicker> = (props) => {
const {
buttonClassName,
className,
closeOnSelect = true,
defaultIconColor = "#5f5f5f",
defaultOpen = EmojiIconPickerTypes.EMOJI,
disabled = false,
dropdownClassName,
label,
onChange,
placement = "bottom-start",
searchPlaceholder = "Search",
theme,
} = props;
// refs
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
// popper-js
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement,
modifiers: [
{
name: "preventOverflow",
options: {
padding: 20,
},
},
],
});

return (
<Popover as="div" className={cn("relative", className)}>
{({ close }) => (
<>
<Popover.Button as={React.Fragment}>
<button
type="button"
ref={setReferenceElement}
className={cn("outline-none", buttonClassName)}
disabled={disabled}
>
{label}
</button>
</Popover.Button>
<Popover.Panel className="fixed z-10">
<div
ref={setPopperElement}
style={styles.popper}
{...attributes.popper}
className={cn(
"h-80 w-80 bg-custom-background-100 rounded-md border-[0.5px] border-custom-border-300 overflow-hidden",
dropdownClassName
)}
>
<Tab.Group
as="div"
className="h-full w-full flex flex-col overflow-hidden"
defaultIndex={TABS_LIST.findIndex((tab) => tab.key === defaultOpen)}
>
<Tab.List as="div" className="grid grid-cols-2 gap-1 p-2">
{TABS_LIST.map((tab) => (
<Tab
key={tab.key}
className={({ selected }) =>
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}
</Tab>
))}
</Tab.List>
<Tab.Panels as="div" className="h-full w-full overflow-y-auto">
<Tab.Panel>
<EmojiPicker
onEmojiClick={(val) => {
onChange({
type: EmojiIconPickerTypes.EMOJI,
value: val,
});
if (closeOnSelect) close();
}}
height="20rem"
width="100%"
theme={theme}
searchPlaceholder={searchPlaceholder}
previewConfig={{
showPreview: false,
}}
/>
</Tab.Panel>
<Tab.Panel>
<IconsList
defaultColor={defaultIconColor}
onChange={(val) => {
onChange({
type: EmojiIconPickerTypes.ICON,
value: val,
});
if (closeOnSelect) close();
}}
/>
</Tab.Panel>
</Tab.Panels>
</Tab.Group>
</div>
</Popover.Panel>
</>
)}
</Popover>
);
};
Loading

0 comments on commit e4f48d6

Please sign in to comment.