Skip to content

Commit

Permalink
switch emoji picker to emojimart and add a proper Ark UI popover style
Browse files Browse the repository at this point in the history
  • Loading branch information
Southclaws committed Dec 27, 2023
1 parent 5f7dbd5 commit 71d0be3
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 56 deletions.
4 changes: 3 additions & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@
"@ark-ui/react": "^0.15.0",
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/sortable": "^7.0.2",
"@emoji-mart/data": "^1.1.2",
"@emoji-mart/react": "^1.1.1",
"@heroicons/react": "^2.0.18",
"@hookform/resolvers": "^3.3.1",
"@simplewebauthn/browser": "^8.0.2",
"@uidotdev/usehooks": "^2.4.1",
"colorjs.io": "^0.4.5",
"date-fns": "^2.29.3",
"emoji-mart": "^5.5.2",
"lodash": "^4.17.21",
"next": "^14.0.0",
"picmo": "^5.8.5",
"polished": "^4.2.2",
"react": "^18.2.0",
"react-avatar-editor": "^13.0.0",
Expand Down
2 changes: 2 additions & 0 deletions web/panda.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { heading } from "src/theme/components/Heading/heading.recipe";
import { input } from "src/theme/components/Input/input.recipe";
import { link } from "src/theme/components/Link/link.recipe";
import { menu } from "src/theme/components/Menu/menu.recipe";
import { popover } from "src/theme/components/Popover/popover.recipe";
import { skeleton } from "src/theme/components/Skeleton/skeleton.recipe";
import { tabs } from "src/theme/components/Tabs/tabs.recipe";
import { titleInput } from "src/theme/components/TitleInput/titleInput.recipe";
Expand Down Expand Up @@ -72,6 +73,7 @@ export default defineConfig({
menu: menu,
tabs: tabs,
checkbox: checkbox,
popover: popover,
skeleton: skeleton,
},
extend: {
Expand Down
2 changes: 1 addition & 1 deletion web/src/components/thread/PostMenu/PostMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function PostMenu(props: PostProps) {

return (
<Menu size="sm">
<MenuTrigger>
<MenuTrigger asChild>
<MoreAction />
</MenuTrigger>

Expand Down
42 changes: 24 additions & 18 deletions web/src/components/thread/ReactList/ReactList.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import { Popover } from "@ark-ui/react";
import data from "@emoji-mart/data";
import Picker from "@emoji-mart/react";
import { PlusIcon } from "@heroicons/react/24/outline";

import { Button } from "src/theme/components/Button";
import {
Popover,
PopoverContent,
PopoverPositioner,
PopoverTrigger,
} from "src/theme/components/Popover";

import { Box, styled } from "@/styled-system/jsx";
import { styled } from "@/styled-system/jsx";

import { Props, emojiPickerContainerID, useReactList } from "./useReactList";
import { Props, useReactList } from "./useReactList";

export function ReactList(props: Props) {
const { onOpen, authenticated } = useReactList(props);
const { authenticated, handlers } = useReactList(props);
return (
<styled.ul
display="flex"
Expand All @@ -26,23 +33,22 @@ export function ReactList(props: Props) {
))}

{authenticated && (
<Popover.Root onOpen={onOpen} portalled>
<Popover.Trigger>
<Popover closeOnInteractOutside closeOnEsc>
<PopoverTrigger asChild>
<Button size="xs" aria-label="add">
<PlusIcon width="1.25em" />
</Button>
</Popover.Trigger>
<Popover.Positioner>
<Popover.Content lazyMount>
<Box
id={`${emojiPickerContainerID}-${props.id}`}
zIndex="dropdown"
>
[emoji picker]
</Box>
</Popover.Content>
</Popover.Positioner>
</Popover.Root>
</PopoverTrigger>

<PopoverPositioner>
<PopoverContent>
<Picker
data={data} //
onEmojiSelect={handlers.onSelect}
/>
</PopoverContent>
</PopoverPositioner>
</Popover>
)}
</styled.ul>
);
Expand Down
36 changes: 12 additions & 24 deletions web/src/components/thread/ReactList/useReactList.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,31 @@
import { NativeRenderer, createPicker } from "picmo";
import { mutate } from "swr";

import { postReactAdd } from "src/api/openapi/posts";
import { PostProps } from "src/api/openapi/schemas";
import { getThreadGetKey } from "src/api/openapi/threads";
import { useSession } from "src/auth";

export const emojiPickerContainerID = `react-emoji-select`;

export type Props = PostProps & {
slug?: string;
};

type EmojiSelectEvent = {
native: string;
};

export function useReactList(props: Props) {
const account = useSession();
const authenticated = !!account;

async function onSelect(event: { emoji: string }) {
await postReactAdd(props.id, { emoji: event.emoji });
async function onSelect(event: EmojiSelectEvent) {
await postReactAdd(props.id, { emoji: event.native });
props.slug && mutate(getThreadGetKey(props.slug));
}

async function onOpen() {
const rootElement = document.querySelector(
`#${emojiPickerContainerID}-${props.id}`,
) as HTMLElement;

if (!rootElement) {
throw new Error("cannot find emoji picker container");
}

const picker = createPicker({
rootElement,
renderer: new NativeRenderer(),
// emojiSize: "1.8rem",
});

picker.addEventListener("emoji:select", onSelect);
}

return { onOpen, authenticated };
return {
authenticated,
handlers: {
onSelect,
},
};
}
42 changes: 42 additions & 0 deletions web/src/theme/components/Popover/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Popover as ArkPopover } from "@ark-ui/react/popover";
import { styled } from "styled-system/jsx";
import { popover } from "styled-system/recipes";

import { createStyleContext } from "src/theme/create-style-context";

const { withProvider, withContext } = createStyleContext(popover);

const Popover = withProvider(ArkPopover.Root);
const PopoverAnchor = withContext(styled(ArkPopover.Anchor), "anchor");
const PopoverArrow = withContext(styled(ArkPopover.Arrow), "arrow");
const PopoverArrowTip = withContext(styled(ArkPopover.ArrowTip), "arrowTip");
const PopoverCloseTrigger = withContext(
styled(ArkPopover.CloseTrigger),
"closeTrigger",
);
const PopoverContent = withContext(styled(ArkPopover.Content), "content");
const PopoverDescription = withContext(
styled(ArkPopover.Description),
"description",
);
// const PopoverIndicator = withContext(styled(ArkPopover.Indicator), "indicator");
const PopoverPositioner = withContext(
styled(ArkPopover.Positioner),
"positioner",
);
const PopoverTitle = withContext(styled(ArkPopover.Title), "title");
const PopoverTrigger = withContext(styled(ArkPopover.Trigger), "trigger");

export {
Popover,
PopoverAnchor,
PopoverArrow,
PopoverArrowTip,
PopoverCloseTrigger,
PopoverContent,
PopoverDescription,
// PopoverIndicator,
PopoverPositioner,
PopoverTitle,
PopoverTrigger,
};
40 changes: 40 additions & 0 deletions web/src/theme/components/Popover/popover.recipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { popoverAnatomy } from "@ark-ui/react";
import { defineSlotRecipe } from "@pandacss/dev";

export const popover = defineSlotRecipe({
className: "popover",
slots: popoverAnatomy.keys(),
base: {
positioner: {
position: "relative",
zIndex: "popover",
},
content: {
borderRadius: "lg",
boxShadow: "lg",
display: "flex",
flexDirection: "column",
maxWidth: "sm",
_open: {
animation: "fadeIn 0.25s ease-out",
},
_closed: {
animation: "fadeOut 0.2s ease-out",
},
_hidden: {
display: "none",
},
},
title: {
fontWeight: "medium",
textStyle: "sm",
},
description: {
color: "fg.muted",
textStyle: "sm",
},
closeTrigger: {
color: "fg.muted",
},
},
});
1 change: 1 addition & 0 deletions web/styled-system/recipes/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export * from './link';
export * from './menu';
export * from './tabs';
export * from './checkbox';
export * from './popover';
export * from './skeleton';
1 change: 1 addition & 0 deletions web/styled-system/recipes/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export * from './link.mjs';
export * from './menu.mjs';
export * from './tabs.mjs';
export * from './checkbox.mjs';
export * from './popover.mjs';
export * from './skeleton.mjs';
28 changes: 28 additions & 0 deletions web/styled-system/recipes/popover.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* eslint-disable */
import type { ConditionalValue } from '../types/index';
import type { Pretty } from '../types/helpers';
import type { DistributiveOmit } from '../types/system-types';

interface PopoverVariant {

}

type PopoverVariantMap = {
[key in keyof PopoverVariant]: Array<PopoverVariant[key]>
}

export type PopoverVariantProps = {
[key in keyof PopoverVariant]?: ConditionalValue<PopoverVariant[key]>
}

export interface PopoverRecipe {
__type: PopoverVariantProps
(props?: PopoverVariantProps): Pretty<Record<"arrow" | "arrowTip" | "anchor" | "trigger" | "positioner" | "content" | "title" | "description" | "closeTrigger", string>>
raw: (props?: PopoverVariantProps) => PopoverVariantProps
variantMap: PopoverVariantMap
variantKeys: Array<keyof PopoverVariant>
splitVariantProps<Props extends PopoverVariantProps>(props: Props): [PopoverVariantProps, Pretty<DistributiveOmit<Props, keyof PopoverVariantProps>>]
}


export declare const popover: PopoverRecipe
62 changes: 62 additions & 0 deletions web/styled-system/recipes/popover.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { splitProps, getSlotCompoundVariant } from '../helpers.mjs';
import { createRecipe } from './create-recipe.mjs';

const popoverDefaultVariants = {}
const popoverCompoundVariants = []

const popoverSlotNames = [
[
"arrow",
"popover__arrow"
],
[
"arrowTip",
"popover__arrowTip"
],
[
"anchor",
"popover__anchor"
],
[
"trigger",
"popover__trigger"
],
[
"positioner",
"popover__positioner"
],
[
"content",
"popover__content"
],
[
"title",
"popover__title"
],
[
"description",
"popover__description"
],
[
"closeTrigger",
"popover__closeTrigger"
]
]
const popoverSlotFns = /* @__PURE__ */ popoverSlotNames.map(([slotName, slotKey]) => [slotName, createRecipe(slotKey, popoverDefaultVariants, getSlotCompoundVariant(popoverCompoundVariants, slotName))])

const popoverFn = (props = {}) => {
return Object.fromEntries(popoverSlotFns.map(([slotName, slotFn]) => [slotName, slotFn(props)]))
}

const popoverVariantKeys = []

export const popover = /* @__PURE__ */ Object.assign(popoverFn, {
__recipe__: false,
__name__: 'popover',
raw: (props) => props,
variantKeys: popoverVariantKeys,
variantMap: {},
splitVariantProps(props) {
return splitProps(props, popoverVariantKeys)
},
})
Loading

2 comments on commit 71d0be3

@vercel
Copy link

@vercel vercel bot commented on 71d0be3 Dec 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 71d0be3 Dec 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.