Skip to content

Commit

Permalink
feat: add photo upload
Browse files Browse the repository at this point in the history
  • Loading branch information
tinaszheng authored and moldy530 committed Jun 25, 2024
1 parent b7173e1 commit b82bdbf
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 26 deletions.
29 changes: 26 additions & 3 deletions examples/ui-demo/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const publicSans = Public_Sans({
const inter = Inter({
subsets: ["latin"],
display: "swap",
})
});

export default function Home() {
const { config } = useConfig();
Expand All @@ -51,7 +51,9 @@ export default function Home() {
className={`flex bg-gray-50 flex-col h-screen ${publicSans.className}`}
>
<TopNav />
<div className={`flex flex-col flex-1 px-10 py-6 gap-6 w-full max-w-screen-2xl mx-auto overflow-hidden ${inter.className}`}>
<div
className={`flex flex-col flex-1 px-10 py-6 gap-6 w-full max-w-screen-2xl mx-auto overflow-hidden ${inter.className}`}
>
{/* Header */}
<div className="flex justify-between items-center">
<h2 className="font-semibold text-xl">Demo</h2>
Expand Down Expand Up @@ -82,7 +84,12 @@ export default function Home() {
>
<div className="flex flex-col gap-2 w-[368px]">
<div className="modal bg-white shadow-md">
<AuthCard showSignInText showNavigation sections={sections} />
<AuthCard
header={<AuthCardHeader />}
showSignInText
showNavigation
sections={sections}
/>
</div>
</div>
</div>
Expand All @@ -91,3 +98,19 @@ export default function Home() {
</main>
);
}

function AuthCardHeader() {
const {
config: {
ui: { logoDark, logoLight, theme },
},
} = useConfig();

const logo = theme === "dark" ? logoDark : logoLight;

if (!logo) return null;

return (
<img style={{ height: "60px" }} src={logo.fileSrc} alt={logo.fileName} />
);
}
10 changes: 10 additions & 0 deletions examples/ui-demo/src/app/state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ export type Config = {
primaryColor: string;
borderRadius: 'none' | 'sm' | 'md' | 'lg';
illustrationStyle: number;
logoLight: {
fileName: string;
fileSrc: string;
} | undefined;
logoDark: {
fileName: string;
fileSrc: string;
} | undefined;
}
}

Expand All @@ -27,6 +35,8 @@ export const DEFAULT_CONFIG: Config = {
primaryColor: '#363FF9',
borderRadius: 'none',
illustrationStyle: 0,
logoLight: undefined,
logoDark: undefined,
},
}

Expand Down
82 changes: 82 additions & 0 deletions examples/ui-demo/src/components/configuration/PhotoUpload.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { useConfig } from "@/src/app/state";
import { PhotoIcon } from "../icons/photo";
import FileUploadInput from "../shared/FileUploadInput";
import { ChangeEvent } from "react";

const LENGTH = 10;
function truncatedFileName(name: string) {
if (name.length < LENGTH) return name;

return `${name.slice(0, LENGTH - 2)}...`;
}

export function PhotoUploads({ mode }: { mode: "dark" | "light" }) {
const { config, setConfig } = useConfig();

const logo = mode === "dark" ? config.ui.logoDark : config.ui.logoLight;

const onUpload = (e: ChangeEvent<HTMLInputElement>) => {
if (!e.target.files || e.target.files.length < 1) return;

const file = e.target.files[0];
setConfig((prev) => ({
...prev,
ui: {
...prev.ui,
[mode === "dark" ? "logoDark" : "logoLight"]: {
fileName: file.name,
fileSrc: URL.createObjectURL(file),
},
},
}));
};

const onRemove = () => {
if (!logo?.fileSrc) return

setConfig((prev) => ({
...prev,
ui: {
...prev.ui,
[mode === "dark" ? "logoDark" : "logoLight"]: undefined,
},
}));
URL.revokeObjectURL(logo.fileSrc)
}

return (
<div className="flex gap-3 flex-1 basis-0">
<div
className={`flex items-center justify-center h-[56px] w-[56px] rounded-xl ${
mode === "light" ? "bg-gray-100" : "bg-gray-500"
}`}
style={{
backgroundImage: logo?.fileSrc ? `url(${logo.fileSrc})` : undefined,
backgroundSize: "cover",
}}
>
{logo?.fileSrc ? null : (
<PhotoIcon color={mode === "dark" ? "white" : undefined} />
)}
</div>
<div className="flex flex-col gap-[2px]">
<div className="text-fg-secondary text-xs font-semibold">
{mode === "light" ? "Light" : "Dark"} mode
</div>
<div className="text-xs text-gray-500 font-medium">
{logo?.fileName ? truncatedFileName(logo.fileName) : "File name"}
</div>
{logo ? (
<button onClick={onRemove} className="text-left text-blue-600 text-xs font-semibold">Remove</button>
) : (
<FileUploadInput
className="text-left text-blue-600 text-xs font-semibold"
onChange={onUpload}
>
Upload
</FileUploadInput>
)}
</div>
</div>
);
}
24 changes: 1 addition & 23 deletions examples/ui-demo/src/components/configuration/Styling.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { cn } from "@/lib/utils";
import { ColorPicker } from "./ColorPicker";
import { useConfig } from "@/src/app/state";
import { PhotoIcon } from "../icons/photo";
import { PhotoUploads } from "./PhotoUpload";

export function Styling({ className }: { className?: string }) {
return (
Expand Down Expand Up @@ -106,26 +107,3 @@ export function CornerRadiusOptions() {
</div>
);
}

export function PhotoUploads({ mode }: { mode: "dark" | "light" }) {
return (
<div className="flex gap-3 flex-1 basis-0">
<div
className={`flex items-center justify-center h-[56px] w-[56px] rounded-xl ${
mode === "light" ? "bg-gray-100" : "bg-gray-500"
}`}
>
<PhotoIcon color={mode === "dark" ? "white" : undefined} />
</div>
<div className="flex flex-col gap-[2px]">
<div className="text-fg-secondary text-xs font-semibold">
{mode === "light" ? "Light" : "Dark"} mode
</div>
<div className="text-xs text-gray-500 font-medium">File name</div>
<button className="p-0 text-left text-blue-600 text-xs font-semibold">
Upload
</button>
</div>
</div>
);
}
52 changes: 52 additions & 0 deletions examples/ui-demo/src/components/shared/FileUploadInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"use client"

import React, { ChangeEvent, useRef } from "react"

interface FileUploadInputProps {
children: React.ReactNode
onChange: (event: ChangeEvent<HTMLInputElement>) => void
accept?: string
[key: string]: any
className?: string
}

const FileUploadInput: React.FC<FileUploadInputProps> = ({
children,
onChange,
accept = "image/*",
className,
...props
}) => {
const ref = useRef<HTMLInputElement>(null)

const selectImage = () => {
if (ref.current) {
// reset the current value so that the input's onChange handler fires even if the
// user keeps selecting the same file
ref.current.value = ""
ref.current.click()
}
}

return (
<button
className={className}
onClick={selectImage}
onKeyDown={selectImage}
type="button"
>
{children}
<input
{...props}
ref={ref}
onChange={onChange}
style={{ display: "none" }}
type="file"
size={10000}
accept={accept}
/>
</button>
)
}

export default FileUploadInput

0 comments on commit b82bdbf

Please sign in to comment.