Skip to content

Commit

Permalink
main: goopin
Browse files Browse the repository at this point in the history
  • Loading branch information
SomeHats committed Feb 3, 2024
1 parent ac42036 commit ddea532
Show file tree
Hide file tree
Showing 34 changed files with 2,008 additions and 15 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"dependencies": {
"@babel/generator": "^7.23.5",
"@babel/types": "^7.23.5",
"@floating-ui/react-dom": "^2.0.8",
"@headlessui/react": "^1.7.2",
"@juggle/resize-observer": "^3.4.0",
"@radix-ui/react-slot": "^1.0.2",
Expand Down
Empty file added src/emoji/Emoji.tsx
Empty file.
211 changes: 211 additions & 0 deletions src/emoji/EmojiListing.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
import {
DrawEmoji,
Emoji,
characters,
colors,
createDrawEmoji,
emotions,
} from "@/emoji/drawEmoji";
import { DebugCanvas } from "@/lib/react/DebugCanvasComponent";
import { keys } from "@/lib/utils";
import { RadioGroup } from "@headlessui/react";
import classNames from "classnames";
import { Fragment, useEffect, useState } from "react";

export function EmojiListing() {
const [drawEmoji, setDrawEmoji] = useState<DrawEmoji>();
const [emoji, setEmoji] = useState<Emoji>({
character: "blob",
emotion: 0,
color: { name: "auto", level: 40 },
});

useEffect(() => {
let isCancelled = false;
void (async () => {
const drawEmoji = await createDrawEmoji();
if (!isCancelled) {
setDrawEmoji(() => drawEmoji);
}
})();
return () => {
isCancelled = true;
};
}, []);

if (!drawEmoji) {
return <div>Loading...</div>;
}

return (
<>
<div className="flex p-1 gap-1">
<RadioGroup
value={emoji.character}
onChange={(character) => setEmoji({ ...emoji, character })}
className="flex bg-stone-200 rounded p-1 gap-1"
>
{characters.map((character, index) => (
<RadioGroup.Option
key={index}
value={character}
className="rounded-sm px-2 ui-checked:bg-stone-50 hover:bg-stone-100 cursor-pointer"
>
{character}
</RadioGroup.Option>
))}
</RadioGroup>
<RadioGroup
value={emoji.color.name}
onChange={(color) =>
setEmoji({
...emoji,
color: { ...emoji.color, name: color },
})
}
className="flex bg-stone-200 rounded p-1 gap-1"
>
{[...keys(colors)].map((color, index) => (
<RadioGroup.Option
key={index}
value={color}
className="rounded-sm px-2 ui-checked:bg-stone-50 hover:bg-stone-100 cursor-pointer"
>
{color}
</RadioGroup.Option>
))}
</RadioGroup>
<RadioGroup
value={emoji.emotion}
onChange={(emotion) =>
setEmoji({
...emoji,
emotion,
})
}
className="flex bg-stone-200 rounded p-1 gap-1"
>
{emotions.map((emotion, index) => (
<RadioGroup.Option
key={index}
value={emotion}
className="rounded-sm px-2 ui-checked:bg-stone-50 hover:bg-stone-100 cursor-pointer"
>
{emotion}
</RadioGroup.Option>
))}
</RadioGroup>
</div>
<CrossFade
value={emoji}
render={(emoji) => (
<EmojiRender
sizePx={256}
emoji={emoji}
drawEmoji={drawEmoji}
/>
)}
/>
<div className="flex">
{[...keys(colors)].map((color, index) => (
<div key={index} className="flex flex-col">
{characters.map((character, index) => (
<Fragment key={index}>
{emotions.map((emotion, index) => (
<div key={index}>
<EmojiRender
sizePx={128}
emoji={{
color: {
name: color,
level: 40,
},
character,
emotion,
}}
drawEmoji={drawEmoji}
/>
</div>
))}
</Fragment>
))}
</div>
))}
</div>
</>
);
}

function EmojiRender({
sizePx,
emoji,
drawEmoji,
}: {
sizePx: number;
emoji: Emoji;
drawEmoji: DrawEmoji;
}) {
return (
<DebugCanvas
width={sizePx}
height={sizePx}
draw={(c) => {
drawEmoji(c.ctx, emoji, sizePx);
}}
/>
);
}

function CrossFade<T>({
value,
render,
className = "relative",
itemClassName = "bg-stone-100",
}: {
value: T;
render: (value: T) => JSX.Element;
className?: string;
itemClassName?: string;
}) {
const [state, setState] = useState({
items: [{ value, index: 0 }],
index: 0,
});

if (state.items[state.items.length - 1].value !== value) {
setState({
items: [...state.items, { value, index: state.index + 1 }],
index: state.index + 1,
});
}

return (
<div className={className}>
{state.items.map((item, i) => {
const isLast = i === state.items.length - 1;
return (
<div
key={item.index}
className={classNames(
"animate-[fade_0.2s_both]",
itemClassName,
isLast ? "relative z-10" : (
"absolute top-0 left-0 z-0"
),
)}
onAnimationEnd={() => {
setState((prev) => ({
...prev,
items: prev.items.filter(
(i) => i.index >= item.index,
),
}));
}}
>
{render(item.value)}
</div>
);
})}
</div>
);
}
53 changes: 53 additions & 0 deletions src/emoji/Post.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { ReactNode } from "react";

export function Post({
name = "Charlie Yeti",
funZone,
}: {
name?: string;
funZone?: ReactNode;
}) {
return (
<div className="bg-white rounded-md shadow-sm text-sm">
<div className="flex items-center gap-3 px-5 py-2 pt-5">
<div className="flex-none aspect-square rounded-full bg-gray-300 w-8 flex items-center justify-center">
{initials(name)}
</div>
<div className="flex-auto text-xs">
<div className="font-semibold">{name}</div>
<div className="text-gray-600">Jan 2 at 7:46pm</div>
</div>
</div>
<div className="flex flex-col gap-2 px-5 py-2">
<p>
A spectre is haunting Europe — the spectre of communism. All
the powers of old Europe have entered into a holy alliance
to exorcise this spectre: Pope and Tsar, Metternich and
Guizot, French Radicals and German police-spies.
</p>
<p>
Where is the party in opposition that has not been decried
as communistic by its opponents in power? Where is the
opposition that has not hurled back the branding reproach of
communism, against the more advanced opposition parties, as
well as against its reactionary adversaries?
</p>
</div>
<div className="flex border-t mt-2 mx-5 py-3">
{funZone}
<button className="ml-auto text-xs hover:bg-gray-100 rounded px-3 py-2">
0 comments
</button>
</div>
</div>
);
}

function initials(name: string) {
return name
.split(" ")
.filter(Boolean)
.map((word) => word[0].toUpperCase())
.join("")
.slice(0, 2);
}
Loading

0 comments on commit ddea532

Please sign in to comment.