Skip to content

Commit

Permalink
feat: updated extract color plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
fluid-design-io committed Oct 25, 2023
1 parent 291532d commit f192871
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 90 deletions.
3 changes: 2 additions & 1 deletion apps/web/components/core/color-picker-fab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ function ColorPickerFab() {
)}
>
<div
id="color-picker-fab"
className={cn(
"pointer-events-auto flex max-w-xs items-center justify-center gap-4 rounded-full border border-white/10 p-2 shadow-lg backdrop-blur-xl backdrop-brightness-105 backdrop-saturate-150 dark:border-white/5 dark:backdrop-brightness-95",
"pointer-events-auto flex max-w-xs items-center justify-center gap-4 rounded-full border border-white/10 p-2 shadow-lg backdrop-blur-xl backdrop-brightness-105 backdrop-saturate-150 transition-opacity dark:border-white/5 dark:backdrop-brightness-95",
// "bg-background-accent/75",
)}
style={{
Expand Down
2 changes: 1 addition & 1 deletion apps/web/components/core/toobar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function Toolbar() {
"relative z-[41] flex select-none flex-row items-center justify-between bg-background-accent py-2 transition-colors sm:z-40",
"fixed inset-x-0 bottom-0 w-full border-t border-t-border",
"sm:border-t-none sm:sticky sm:inset-x-auto sm:bottom-auto sm:top-0 sm:border-none",
"site-padding",
"site-padding pointer-events-auto",
)}
>
<div className="flex items-center justify-end gap-4">
Expand Down
36 changes: 1 addition & 35 deletions apps/web/components/toolbar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,8 @@
"use client";

import ToolbarUploadImage from "./upload-image";
import ToolbarDownloadBasePalette from "./download-base-palette";
import ToolbarShareableLink from "./shareable-link";
import { Fragment } from "react";
import { useToolStore } from "@/store/toolStore";
import { createPortal } from "react-dom";

import dynamic from "next/dynamic";
import useStore from "@/store/useStore";
import { AnimatePresence } from "framer-motion";

const UploadImaagePluginDialogContent = dynamic(
() => import("@/components/toolbar/plugin/upload-image.plugin"),
{
loading: () => null,
ssr: false,
},
);

const ToolbarPrimative = ({ children }) => {
const openImageColorExtractor = useStore(
useToolStore,
(state) => state.openImageColorExtractor,
);
return (
<Fragment>
{children}
{!!openImageColorExtractor &&
createPortal(
<UploadImaagePluginDialogContent
key={`uipd-${openImageColorExtractor}`}
/>,
document.body,
)}
</Fragment>
);
};
const ToolbarPrimative = ({ children }) => children;

ToolbarPrimative.displayName = "Toolbar";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ function DownloadBasePalettePlugin({ setOpen }) {
exit={{ opacity: 0, scale: 0.97, filter: "blur(6px)" }}
src={imageData}
className={cn(
"absolute inset-0 h-full w-full rounded border object-cover transition-opacity",
"absolute inset-0 h-full w-full rounded border object-cover",
)}
alt="Social preview"
width={288}
Expand Down
13 changes: 3 additions & 10 deletions apps/web/components/toolbar/plugin/shareable-link.plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { Skeleton } from "@ui/components/ui/skeleton";
import { cn } from "ui/lib/utils";
import Image from "next/image";
import { useToast } from "ui/components/ui/use-toast";
import { motion } from "framer-motion";

function ShareableLinkPlugin({ colors, setOpen }) {
const { toast } = useToast();
Expand All @@ -24,12 +23,7 @@ function ShareableLinkPlugin({ colors, setOpen }) {
};
return (
<Fragment>
<motion.div
initial={{ opacity: 0, scale: 0.97, filter: "blur(6px)" }}
animate={{ opacity: 1, scale: 1, filter: "blur(0px)" }}
exit={{ opacity: 0, scale: 0.97, filter: "blur(6px)" }}
className="relative"
>
<div className="relative">
<div
className={cn(
"mb-4 overflow-hidden rounded-md",
Expand All @@ -44,16 +38,15 @@ function ShareableLinkPlugin({ colors, setOpen }) {
<Image
src={`${process.env.NEXT_PUBLIC_URL}/api/og?colors=${colors}`}
className={cn(
"absolute inset-0 h-full w-full rounded border object-cover transition-opacity",
loadingSocialPreview ? "opacity-0" : "opacity-100",
"absolute inset-0 h-full w-full rounded border object-cover",
)}
alt="Social preview"
onLoad={() => setLoadingSocialPreview(false)}
width={288}
height={151}
/>
</div>
</motion.div>
</div>
<div className="flex space-x-2">
<Input
className="h-8"
Expand Down
120 changes: 92 additions & 28 deletions apps/web/components/toolbar/plugin/upload-image.plugin.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { Button } from "ui/components/ui/button";
import React, { Fragment, useEffect, useRef, useState } from "react";
import React, { Fragment, memo, useEffect, useRef, useState } from "react";
import { cn } from "ui/lib/utils";
import { useToast } from "ui/components/ui/use-toast";
import {
Expand All @@ -21,8 +21,9 @@ import { BaseColorTypes } from "@/types/app";
import { FinalColor } from "extract-colors/lib/types/Color";
import { useToolStore } from "@/store/toolStore";
import { XCircleIcon } from "lucide-react";
import { usePathname } from "next/navigation";

function UploadImaagePlugin() {
function UploadImaagePlugin(props) {
const { updateBaseColor, generatePalette } = useColorStore();
const {
setOpenImageColorExtractor,
Expand All @@ -32,6 +33,8 @@ function UploadImaagePlugin() {
const [activeColorIndex, setActiveColorIndex] = useState(0);
const [preview, setPreview] = useState(null);
const { toast } = useToast();
const pathname = usePathname();
const isRoot = pathname === "/";

/* Framer bottom sheet */
const [isCollapsed, setIsCollapsed] = useState(false);
Expand All @@ -53,6 +56,11 @@ function UploadImaagePlugin() {
[sheetHeight / 4, sheetHeight],
[0.5, 0.92],
);
const collapsePreviewOpacity = useTransform(
sheetYSpring,
[sheetHeight / 4, sheetHeight],
[0, isDesktopSize ? 0.8 : 0.4],
);
const sheetBackdropBlur = useTransform(
sheetYSpring,
[sheetHeight / 4, sheetHeight],
Expand All @@ -69,14 +77,19 @@ function UploadImaagePlugin() {
[0, sheetHeight],
[0, -innerSheetWidth / 2 + PILL_WIDTH / 2],
);
const sheetX = useTransform(
sheetYSpring,
[0, sheetHeight],
[0, isRoot ? 0 : 172],
);
const sheetRadius = useTransform(
sheetYSpring,
[0, sheetHeight],
[0, PILL_WIDTH / 4],
);
const sheetInnerOpacity = useTransform(
sheetYSpring,
[0, sheetHeight],
[0, sheetHeight / 1.5],
[1, 0],
);

Expand All @@ -87,12 +100,30 @@ function UploadImaagePlugin() {
setIsCollapsed(true);
controls.start("collapse");
sheetY.set(sheetHeight);
if (!isRoot) {
// find #color-picker-fab
const fab = document?.getElementById("color-picker-fab");
if (fab) {
// make it visible and enable pointer events
fab.style.opacity = "1";
fab.style.pointerEvents = "auto";
}
}
};

const onExpand = () => {
setIsCollapsed(false);
controls.start("expand");
sheetY.set(0);
if (!isRoot) {
// find #color-picker-fab
const fab = document?.getElementById("color-picker-fab");
if (fab) {
// make it invisible and disable pointer events
fab.style.opacity = "0";
fab.style.pointerEvents = "none";
}
}
};

const onHidden = () => {
Expand All @@ -103,6 +134,15 @@ function UploadImaagePlugin() {
baseColors: [],
colors: [],
});
if (!isRoot) {
// find #color-picker-fab
const fab = document?.getElementById("color-picker-fab");
if (fab) {
// make it visible and enable pointer events
fab.style.opacity = "1";
fab.style.pointerEvents = "auto";
}
}
}, 700);
};

Expand All @@ -119,14 +159,14 @@ function UploadImaagePlugin() {
function onDrag(event, info) {
// If the modal content is scrollable, we need to get more information.
if (
scrollAreaRef.current.scrollHeight > scrollAreaRef.current.clientHeight
scrollAreaRef?.current?.scrollHeight >
scrollAreaRef?.current?.clientHeight
) {
// If the modal is already collapsed or if the user has scrolled back to the top,
// then we can allow the drag event to collapse the modal.
if (isCollapsed || scrollAreaRef.current.scrollTop === 0) {
if (isCollapsed || scrollAreaRef?.current?.scrollTop === 0) {
const y = info.offset.y;
if (y < 0 && !isCollapsed) {
console.log(`disable drag`);
setEnableDrag(false);
return;
}
Expand Down Expand Up @@ -155,12 +195,11 @@ function UploadImaagePlugin() {
}, []);

const calcScreenDimension = () => {
const h = sheetRef.current.offsetHeight;
const h = sheetRef?.current?.offsetHeight;
const w = window.innerWidth;
// isCollapsed && onExpand();
setSheetHeight(h);
setInnerSheetWidth(w);
console.log(`sheet height: ${h}, sheet width: ${w}`);
};
useEffect(() => {
window.addEventListener("resize", calcScreenDimension);
Expand All @@ -177,17 +216,17 @@ function UploadImaagePlugin() {
useEffect(() => {
if (isCollapsed) return;
const handleFocus = (e) => {
if (!sheetRef.current.contains(e.target)) {
sheetRef.current.focus();
if (!sheetRef?.current?.contains(e.target)) {
sheetRef?.current?.focus();
}
};
document.addEventListener("focus", handleFocus, true);
document.body.style.overflow = "hidden";
document.body.style.pointerEvents = "none";
// document.body.style.pointerEvents = "none";
return () => {
document.removeEventListener("focus", handleFocus, true);
document.body.style.overflow = "auto";
document.body.style.pointerEvents = "auto";
// document.body.style.pointerEvents = "auto";
};
}, [isCollapsed]);

Expand Down Expand Up @@ -293,6 +332,8 @@ function UploadImaagePlugin() {
WebkitBackdropFilter: sheetFilterStyle,
width: isDesktopSize ? sheetWidth : "100%",
borderRadius: sheetRadius,
x: sheetX,
touchAction: "pan-y",
}}
drag={enableDrag ? "y" : false}
onDrag={onDrag}
Expand All @@ -315,20 +356,47 @@ function UploadImaagePlugin() {
dragConstraints={{ top: 0 }}
dragElastic={isCollapsed ? 0.2 : 0}
ref={sheetRef}
// expand if drop enter
onDragOver={() => isCollapsed && onExpand()}
{...props}
>
{/* Handle */}
<button
type="button"
className="group absolute inset-x-0 top-0 z-10 mx-auto h-1 w-12 cursor-row-resize pt-2 md:h-1.5"
className={cn(
"group absolute inset-x-0 top-0 z-[21] mx-auto h-1 w-12 cursor-row-resize pt-2 transition-all md:h-1.5",
!isRoot && isCollapsed && !isDesktopSize && "left-auto right-8",
)}
onClick={handleSheetPosition}
>
<span
className={cn(
"absolute inset-0 top-2 h-1 w-full rounded-full bg-foreground/20 transition-colors sm:h-1.5",
"absolute inset-0 top-2 h-1 w-full rounded-full transition-colors sm:h-1.5",
"group-hover:bg-foreground/50 group-focus:bg-foreground/50",
"backdrop-blur-md backdrop-brightness-75 transition-opacity",
isCollapsed ? "bg-foreground/20" : "bg-foreground/0",
)}
/>
<span className="sr-only">Click to minimize</span>
<span className="sr-only">
Click to {isCollapsed ? "expand" : "collapse"} the sheet
</span>
</button>
<motion.div
className="pointer-events-none absolute inset-x-0 top-0 z-20 mx-auto bg-background/10"
style={{
opacity: collapsePreviewOpacity,
}}
>
{preview && (
<motion.img
style={{
opacity: collapsePreviewOpacity,
}}
src={preview}
className="h-full w-full object-cover object-bottom"
/>
)}
</motion.div>
<motion.div
className="flex items-center justify-between"
style={{
Expand Down Expand Up @@ -359,7 +427,7 @@ function UploadImaagePlugin() {
<motion.div
className={cn(
"mt-4 grid gap-4 sm:gap-6 lg:gap-8",
"max-h-[calc(100dvh-5.5rem)] overflow-y-auto overflow-x-visible px-4 pb-6 sm:px-6 lg:px-8",
"max-h-[calc(100dvh-8rem)] overflow-y-auto overflow-x-visible px-4 pb-6 sm:px-6 lg:px-8",
"w-[calc(100dvw-env(safe-area-inset-right, 0px)-env(safe-area-inset-left, 0px))]",
!!preview
? "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3"
Expand All @@ -373,16 +441,16 @@ function UploadImaagePlugin() {
ref={scrollAreaRef}
onScroll={(e) => {
// enable drag if the user has scrolled back to the top
if (e.currentTarget.scrollTop === 0) {
if (e.currentTarget.scrollTop <= 10) {
setEnableDrag(true);
} else if (e.currentTarget.scrollTop > 0) {
} else if (e.currentTarget.scrollTop > 10) {
setEnableDrag(false);
}
}}
>
<div
className={cn(
"min-h-64 relative isolate aspect-[3/2] max-h-[24rem] w-full sm:col-span-2 lg:col-span-1",
"min-h-64 relative isolate h-[20rem] w-full sm:col-span-2 lg:col-span-1",
)}
>
<AnimatePresence mode="wait">
Expand Down Expand Up @@ -455,14 +523,10 @@ function UploadImaagePlugin() {
>
<div className="flex items-center justify-between">
<h3 className="text-lg font-semibold">Colors</h3>
<Button
variant="ghost"
size="sm"
className="-mr-1"
onClick={onHidden}
>
Clear
</Button>
<p className="text-xs text-muted-foreground/70">
Click to replace{" "}
{["primary", "secondary", "accent"][activeColorIndex]} color
</p>
</div>

{!!preview && colors.length === 0 && (
Expand Down Expand Up @@ -508,4 +572,4 @@ function UploadImaagePlugin() {

UploadImaagePlugin.displayName = "UploadImaagePlugin";

export default UploadImaagePlugin;
export default memo(UploadImaagePlugin);
Loading

0 comments on commit f192871

Please sign in to comment.