From 57478ac0b4167ed15952ec58b0adde77791a126b Mon Sep 17 00:00:00 2001 From: nnecec Date: Mon, 18 Dec 2023 18:32:21 +0800 Subject: [PATCH 1/2] feat: background --- .prettierrc | 1 - .prettierrc.cjs | 3 + app/page.tsx | 137 +- .../{presets => frame-presets}/index.ts | 0 .../{presets => frame-presets}/polaroid.ts | 10 +- .../xiaomi-leica.ts | 7 +- core/editor/frame.tsx | 2 +- core/editor/photo.tsx | 4 +- core/editor/store.ts | 2 + .../tools/board-background-eyedropper.tsx | 33 + core/editor/tools/board-background-mesh.tsx | 67 + .../editor/tools/board-background-palette.tsx | 22 + .../tools/board-background-prominent.tsx | 44 + core/editor/tools/board-background.tsx | 107 - core/editor/tools/export-button.tsx | 94 +- core/editor/tools/frame-mode.tsx | 4 +- core/editor/tools/frame-scale.tsx | 2 +- core/editor/tools/index.tsx | 5 +- core/types/global.d.ts | 19 + core/ui/button.tsx | 3 +- core/ui/color/mesh.ts | 17 +- core/utils/exif.ts | 10 +- core/utils/use-event-listener.ts | 10 +- core/utils/use-eyedropper.ts | 89 + core/utils/use-resize-observer.ts | 30 +- package.json | 30 +- pnpm-lock.yaml | 1716 +++++++++-------- public/favicon.ico | Bin 15086 -> 10659 bytes public/vercel.svg | 4 - tsconfig.json | 9 +- 30 files changed, 1369 insertions(+), 1112 deletions(-) delete mode 100644 .prettierrc create mode 100644 .prettierrc.cjs rename core/editor/{presets => frame-presets}/index.ts (100%) rename core/editor/{presets => frame-presets}/polaroid.ts (91%) rename core/editor/{presets => frame-presets}/xiaomi-leica.ts (63%) create mode 100644 core/editor/tools/board-background-eyedropper.tsx create mode 100644 core/editor/tools/board-background-mesh.tsx create mode 100644 core/editor/tools/board-background-palette.tsx create mode 100644 core/editor/tools/board-background-prominent.tsx delete mode 100644 core/editor/tools/board-background.tsx create mode 100644 core/types/global.d.ts create mode 100644 core/utils/use-eyedropper.ts delete mode 100644 public/vercel.svg diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 01aa840..0000000 --- a/.prettierrc +++ /dev/null @@ -1 +0,0 @@ -"@nnecec/prettier-config" diff --git a/.prettierrc.cjs b/.prettierrc.cjs new file mode 100644 index 0000000..65d8ff0 --- /dev/null +++ b/.prettierrc.cjs @@ -0,0 +1,3 @@ +module.exports = { + ...require('@nnecec/prettier-config'), +} diff --git a/app/page.tsx b/app/page.tsx index 838f897..48f73df 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,15 +1,16 @@ 'use client' -import { useState } from 'react' - import clsx from 'clsx' import { AnimatePresence, motion } from 'framer-motion' -import { ChevronLeftIcon, ChevronRightIcon, UploadIcon } from '@radix-ui/react-icons' +import { UploadIcon } from '@radix-ui/react-icons' import { Board, - BoardBackground, + BoardBackgroundEyedropper, + BoardBackgroundMesh, + BoardBackgroundPalette, + BoardBackgroundProminent, BoardPadding, ExportButton, Frame, @@ -19,87 +20,73 @@ import { PhotoBorderRadius, } from '~/core/editor' import { robotoMono } from '~/core/fonts' -import { Button, Tabs, TabsContent, TabsList, TabsTrigger } from '~/core/ui' +import { Tabs, TabsContent, TabsList, TabsTrigger } from '~/core/ui' export default function Page() { - const [hideSidebar, setHideSidebar] = useState(false) - return (
- {!hideSidebar && ( - -
-
-

Deco

- - - Board - Frame - - - - - - - - - - - -
-
- - + +
+
+

Deco

+ + + + Board + Frame + + + + + + + + + + + + + + +
+ - - )} +
+
- +
- - - - - - - - -
- -

- Upload to{' '} - - Deco your Artwork - -

-
+ + + +
+ +

+ Upload to{' '} + + Deco your Artwork + +

- } - /> - -
-
+
+ } + /> + +
{/* */} diff --git a/core/editor/presets/index.ts b/core/editor/frame-presets/index.ts similarity index 100% rename from core/editor/presets/index.ts rename to core/editor/frame-presets/index.ts diff --git a/core/editor/presets/polaroid.ts b/core/editor/frame-presets/polaroid.ts similarity index 91% rename from core/editor/presets/polaroid.ts rename to core/editor/frame-presets/polaroid.ts index 956a417..ea01b4a 100644 --- a/core/editor/presets/polaroid.ts +++ b/core/editor/frame-presets/polaroid.ts @@ -2,18 +2,18 @@ import type { FrameMode } from '~/core/utils/types' export const PolaroidFrame: FrameMode = { frame: { - className: 'pt-8 pb-24 px-6 bg-white', + className: 'pt-6 pb-28 px-6 bg-white', items: [ { - children: '1989', + children: 'T.S', props: { - className: 'absolute text-black bottom-6 right-6 text-4xl', + className: 'absolute text-black bottom-6 left-6 text-4xl', }, }, { - children: 'T.S', + children: '1989', props: { - className: 'absolute text-black bottom-6 left-6 text-4xl', + className: 'absolute text-black bottom-6 right-6 text-4xl', }, }, ], diff --git a/core/editor/presets/xiaomi-leica.ts b/core/editor/frame-presets/xiaomi-leica.ts similarity index 63% rename from core/editor/presets/xiaomi-leica.ts rename to core/editor/frame-presets/xiaomi-leica.ts index 75b807d..37f1b42 100644 --- a/core/editor/presets/xiaomi-leica.ts +++ b/core/editor/frame-presets/xiaomi-leica.ts @@ -5,16 +5,17 @@ export const XiaomiLeicaFrame: FrameMode = { className: 'pb-20 bg-white', items: [ { - children: 'Xiaomi x Leica', + children: 'XIAOMI 14 PRO', props: { - className: 'absolute text-black bottom-5 left-5', + className: 'absolute text-black bottom-6 left-5 font-bold', }, }, { children: 'logo', props: { - className: 'absolute text-black bottom-5 right-5', + className: 'absolute text-black bottom-6 right-5', }, + type: 'svg', }, ], }, diff --git a/core/editor/frame.tsx b/core/editor/frame.tsx index 50aeceb..ce31840 100644 --- a/core/editor/frame.tsx +++ b/core/editor/frame.tsx @@ -42,7 +42,7 @@ export const Frame = ({ children, className }: PropsWithChildren) => drag: true, dragMomentum: false, ...item.props, - className: 'hover:cursor-grab active:cursor-grabbing', + className: clsx(item.props.className, 'hover:cursor-grab active:cursor-grabbing'), } as HTMLMotionProps<'div'>, item.children, ) diff --git a/core/editor/photo.tsx b/core/editor/photo.tsx index e8be11d..85de89c 100644 --- a/core/editor/photo.tsx +++ b/core/editor/photo.tsx @@ -41,7 +41,9 @@ export const Photo = ({ className, placeholder }: PropsWithChildren) }} >
diff --git a/core/editor/store.ts b/core/editor/store.ts index 5533e43..9c288d7 100644 --- a/core/editor/store.ts +++ b/core/editor/store.ts @@ -17,6 +17,8 @@ export const photoBlurVignetteBlurAtom = atom(0) // board export const boardPaddingAtom = atom({ x: 0, y: 0 }) export const boardBackgroundImageAtom = atom('none') +export const boardBackgroundMeshOffsetAtom = atom(1) +export const boardBackgroundMeshEnableAtom = atom(false) export const boardProminentColorsAtom = atom>(async get => { const photo = get(photoSrcAtom) diff --git a/core/editor/tools/board-background-eyedropper.tsx b/core/editor/tools/board-background-eyedropper.tsx new file mode 100644 index 0000000..578a29f --- /dev/null +++ b/core/editor/tools/board-background-eyedropper.tsx @@ -0,0 +1,33 @@ +'use client' + +import { colord } from 'colord' +import { useSetAtom } from 'jotai' + +import { Crosshair1Icon } from '@radix-ui/react-icons' + +import { Button, Label } from '~/core/ui' +import { useEyeDropper } from '~/core/utils/use-eyedropper' + +import { boardBackgroundColorAtom } from '../store' + +export const BoardBackgroundEyedropper = () => { + const setColor = useSetAtom(boardBackgroundColorAtom) + const { open } = useEyeDropper() + + const handleOpen = async () => { + const color = await open() + setColor(colord(color.sRGBHex).toRgb()) + } + + return ( +
+
+ +
+ + +
+ ) +} diff --git a/core/editor/tools/board-background-mesh.tsx b/core/editor/tools/board-background-mesh.tsx new file mode 100644 index 0000000..5465c04 --- /dev/null +++ b/core/editor/tools/board-background-mesh.tsx @@ -0,0 +1,67 @@ +'use client' +import { useEffect, useState } from 'react' + +import { colord } from 'colord' +import { useAtom, useSetAtom } from 'jotai' + +import { SymbolIcon } from '@radix-ui/react-icons' + +import { Button, Label, Slider, Switch } from '~/core/ui' + +import { meshGradient } from '../../ui/color/mesh' +import { + boardBackgroundColorAtom, + boardBackgroundImageAtom, + boardBackgroundMeshEnableAtom, + boardBackgroundMeshOffsetAtom, +} from '../store' + +export const BoardBackgroundMesh = () => { + const [color] = useAtom(boardBackgroundColorAtom) + const setImage = useSetAtom(boardBackgroundImageAtom) + const [offset, setOffset] = useAtom(boardBackgroundMeshOffsetAtom) + const [enable, setEnable] = useAtom(boardBackgroundMeshEnableAtom) + + useEffect(() => { + if (enable) { + const [, bg] = meshGradient(colord(color).toHex(), { amount: 5, offset }) + setImage(bg!) + } else { + setImage('none') + } + }, [color, enable, offset, setImage]) + + return ( +
+
+ + { + setEnable(value) + }} + /> +
+ +
+ {enable ? + <> + + { + setOffset(value[0]!) + }} + step={0.1} + value={[offset]} + /> + + : null} +
+
+ ) +} diff --git a/core/editor/tools/board-background-palette.tsx b/core/editor/tools/board-background-palette.tsx new file mode 100644 index 0000000..21fc165 --- /dev/null +++ b/core/editor/tools/board-background-palette.tsx @@ -0,0 +1,22 @@ +'use client' +import { RgbaColorPicker } from 'react-colorful' + +import { useAtom } from 'jotai' + +import { Label } from '~/core/ui' + +import { boardBackgroundColorAtom } from '../store' + +export const BoardBackgroundPalette = () => { + const [color, setColor] = useAtom(boardBackgroundColorAtom) + + return ( +
+
+ +
+ + +
+ ) +} diff --git a/core/editor/tools/board-background-prominent.tsx b/core/editor/tools/board-background-prominent.tsx new file mode 100644 index 0000000..bc77cc4 --- /dev/null +++ b/core/editor/tools/board-background-prominent.tsx @@ -0,0 +1,44 @@ +'use client' + +import { colord } from 'colord' +import { useAtomValue, useSetAtom } from 'jotai' + +import { Button, Label, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '~/core/ui' + +import { boardBackgroundColorAtom, boardProminentColorsAtom } from '../store' + +export const BoardBackgroundProminent = () => { + const setColor = useSetAtom(boardBackgroundColorAtom) + const colors = useAtomValue(boardProminentColorsAtom) + + return ( +
+
+ +
+ +
+ {colors?.length > 0 ? + colors.map(c => ( + + + + + } +
+
+ ) +} diff --git a/core/editor/tools/board-background.tsx b/core/editor/tools/board-background.tsx deleted file mode 100644 index 624a34b..0000000 --- a/core/editor/tools/board-background.tsx +++ /dev/null @@ -1,107 +0,0 @@ -'use client' -import { useState } from 'react' -import { RgbaColorPicker } from 'react-colorful' - -import { colord } from 'colord' -import { motion } from 'framer-motion' -import { useAtom, useAtomValue, useSetAtom } from 'jotai' - -import { ReloadIcon } from '@radix-ui/react-icons' - -import { Button, Label, Switch, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '~/core/ui' - -import { meshGradient } from '../../ui/color/mesh' -import { boardBackgroundColorAtom, boardBackgroundImageAtom, boardProminentColorsAtom } from '../store' - -const MotionButton = motion(Button) - -export const BoardBackground = () => { - const [color, setColor] = useAtom(boardBackgroundColorAtom) - const setImage = useSetAtom(boardBackgroundImageAtom) - const colors = useAtomValue(boardProminentColorsAtom) - - const [enableMesh, setEnableMesh] = useState(false) - - const mesh = (enable: boolean) => { - if (enable) { - const [, i] = meshGradient(colord(color).toHex(), { amount: 5 }) - setImage(i!) - } else { - setImage('none') - } - } - - return ( -
-
- -
- - - - {colors?.length > 0 && ( - <> -
- -
- -
- {colors.map(c => ( - - - -
- -
- -
-
- { - setEnableMesh(value) - mesh(value) - }} - /> - {enableMesh ? ( - - - - mesh(true)} - size="icon" - whileTap={{ rotate: 60 }} - > - - - - -

regenerate

-
-
-
- ) : null} -
- - )} -
- ) -} diff --git a/core/editor/tools/export-button.tsx b/core/editor/tools/export-button.tsx index 07b582c..e3959c0 100644 --- a/core/editor/tools/export-button.tsx +++ b/core/editor/tools/export-button.tsx @@ -1,5 +1,3 @@ -import { useRef, useState } from 'react' - import { toJpeg } from 'html-to-image' import { faker } from '@faker-js/faker' @@ -21,56 +19,64 @@ const handleExport = () => { } export const ExportButton = (props: ButtonProps) => { - const divRef = useRef(null) - const [isFocused, setIsFocused] = useState(false) - const [position, setPosition] = useState({ x: 0, y: 0 }) - const [opacity, setOpacity] = useState(0) + // const divRef = useRef(null) + // const [isFocused, setIsFocused] = useState(false) + // const [position, setPosition] = useState({ x: 0, y: 0 }) + // const [opacity, setOpacity] = useState(0) - const handleMouseMove = (e: React.MouseEvent) => { - if (!divRef.current || isFocused) return + // const handleMouseMove = (e: React.MouseEvent) => { + // if (!divRef.current || isFocused) return - const div = divRef.current - const rect = div.getBoundingClientRect() + // const div = divRef.current + // const rect = div.getBoundingClientRect() - setPosition({ x: e.clientX - rect.left, y: e.clientY - rect.top }) - } + // setPosition({ x: e.clientX - rect.left, y: e.clientY - rect.top }) + // } - const handleBlur = () => { - setIsFocused(false) - setOpacity(0) - } + // const handleBlur = () => { + // setIsFocused(false) + // setOpacity(0) + // } - const handleMouseEnter = () => { - setOpacity(1) - } + // const handleMouseEnter = () => { + // setOpacity(1) + // } - const handleMouseLeave = () => { - setOpacity(0) - } + // const handleMouseLeave = () => { + // setOpacity(0) + // } return ( -
- -